From 8f8859a5f50fe4f02ec44c3dcb3733735d6af90a Mon Sep 17 00:00:00 2001 From: jainanuj94 Date: Fri, 2 Aug 2024 15:54:03 +0530 Subject: [PATCH 01/93] Initial commit - create a different file for signals --- freqtrade/data/entryexitanalysis.py | 29 +++++++++++++++++-- freqtrade/optimize/backtesting.py | 15 ++++++++-- .../optimize/optimize_reports/__init__.py | 3 +- .../optimize/optimize_reports/bt_storage.py | 7 ++++- .../optimize_reports/optimize_reports.py | 27 +++++++++++++++-- tests/optimize/test_optimize_reports.py | 8 ++--- 6 files changed, 76 insertions(+), 13 deletions(-) diff --git a/freqtrade/data/entryexitanalysis.py b/freqtrade/data/entryexitanalysis.py index e76f2dff752..7b18097df76 100644 --- a/freqtrade/data/entryexitanalysis.py +++ b/freqtrade/data/entryexitanalysis.py @@ -47,9 +47,12 @@ def _load_signal_candles(backtest_dir: Path): return _load_backtest_analysis_data(backtest_dir, "signals") +def _load_exit_signal_candles(backtest_dir: Path): + return _load_backtest_analysis_data(backtest_dir, "exited") + + def _process_candles_and_indicators(pairlist, strategy_name, trades, signal_candles): - analysed_trades_dict = {} - analysed_trades_dict[strategy_name] = {} + analysed_trades_dict = {strategy_name: {}} try: logger.info(f"Processing {strategy_name} : {len(pairlist)} pairs") @@ -333,6 +336,7 @@ def process_entry_exit_reasons(config: Config): if trades is not None and not trades.empty: signal_candles = _load_signal_candles(config["exportfilename"]) + exit_signal_candles = _load_exit_signal_candles(config["exportfilename"]) rej_df = None if do_rejected: @@ -349,6 +353,10 @@ def process_entry_exit_reasons(config: Config): config["exchange"]["pair_whitelist"], strategy_name, trades, signal_candles ) + exited_trades_dict = _process_candles_and_indicators( + config["exchange"]["pair_whitelist"], strategy_name, trades, exit_signal_candles + ) + res_df = prepare_results( analysed_trades_dict, strategy_name, @@ -357,6 +365,23 @@ def process_entry_exit_reasons(config: Config): timerange=timerange, ) + exited_df = prepare_results( + exited_trades_dict, + strategy_name, + enter_reason_list, + exit_reason_list, + timerange=timerange, + ) + + print_results( + exited_df, + analysis_groups, + indicator_list, + to_csv=False, + rejected_signals=None, + csv_path=csv_path, + ) + print_results( res_df, analysis_groups, diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index c28c080f5cb..8beb05ff4f7 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -42,7 +42,8 @@ from freqtrade.optimize.optimize_reports import ( generate_backtest_stats, generate_rejected_signals, - generate_trade_signal_candles, + generate_trade_entry_signal_candles, + generate_trade_exit_signal_candles, show_backtest_results, store_backtest_analysis_results, store_backtest_stats, @@ -122,6 +123,7 @@ def __init__(self, config: Config, exchange: Optional[Exchange] = None) -> None: self.processed_dfs: Dict[str, Dict] = {} self.rejected_dict: Dict[str, List] = {} self.rejected_df: Dict[str, Dict] = {} + self.exited_dfs: Dict[str, Dict] = {} self._exchange_name = self.config["exchange"]["name"] if not exchange: @@ -1558,12 +1560,15 @@ def backtest_one_strategy( self.config.get("export", "none") == "signals" and self.dataprovider.runmode == RunMode.BACKTEST ): - self.processed_dfs[strategy_name] = generate_trade_signal_candles( + self.processed_dfs[strategy_name] = generate_trade_entry_signal_candles( preprocessed_tmp, results ) self.rejected_df[strategy_name] = generate_rejected_signals( preprocessed_tmp, self.rejected_dict ) + self.exited_dfs[strategy_name] = generate_trade_exit_signal_candles( + preprocessed_tmp, results + ) return min_date, max_date @@ -1639,7 +1644,11 @@ def start(self) -> None: and self.dataprovider.runmode == RunMode.BACKTEST ): store_backtest_analysis_results( - self.config["exportfilename"], self.processed_dfs, self.rejected_df, dt_appendix + self.config["exportfilename"], + self.processed_dfs, + self.rejected_df, + self.exited_dfs, + dt_appendix, ) # Results may be mixed up now. Sort them so they follow --strategy-list order. diff --git a/freqtrade/optimize/optimize_reports/__init__.py b/freqtrade/optimize/optimize_reports/__init__.py index 6f3278a1c7e..1dcc1e885b4 100644 --- a/freqtrade/optimize/optimize_reports/__init__.py +++ b/freqtrade/optimize/optimize_reports/__init__.py @@ -25,6 +25,7 @@ generate_strategy_comparison, generate_strategy_stats, generate_tag_metrics, - generate_trade_signal_candles, + generate_trade_entry_signal_candles, + generate_trade_exit_signal_candles, generate_trading_stats, ) diff --git a/freqtrade/optimize/optimize_reports/bt_storage.py b/freqtrade/optimize/optimize_reports/bt_storage.py index ea899133731..ed0667cf4a7 100644 --- a/freqtrade/optimize/optimize_reports/bt_storage.py +++ b/freqtrade/optimize/optimize_reports/bt_storage.py @@ -90,7 +90,12 @@ def _store_backtest_analysis_data( def store_backtest_analysis_results( - recordfilename: Path, candles: Dict[str, Dict], trades: Dict[str, Dict], dtappendix: str + recordfilename: Path, + candles: Dict[str, Dict], + trades: Dict[str, Dict], + exited: Dict[str, Dict], + dtappendix: str, ) -> None: _store_backtest_analysis_data(recordfilename, candles, dtappendix, "signals") _store_backtest_analysis_data(recordfilename, trades, dtappendix, "rejected") + _store_backtest_analysis_data(recordfilename, exited, dtappendix, "exited") diff --git a/freqtrade/optimize/optimize_reports/optimize_reports.py b/freqtrade/optimize/optimize_reports/optimize_reports.py index ef5fce0e195..e82184516f3 100644 --- a/freqtrade/optimize/optimize_reports/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports/optimize_reports.py @@ -24,9 +24,9 @@ logger = logging.getLogger(__name__) -def generate_trade_signal_candles( +def generate_trade_entry_signal_candles( preprocessed_df: Dict[str, DataFrame], bt_results: Dict[str, Any] -) -> DataFrame: +) -> Dict[str, DataFrame]: signal_candles_only = {} for pair in preprocessed_df.keys(): signal_candles_only_df = DataFrame() @@ -47,6 +47,29 @@ def generate_trade_signal_candles( return signal_candles_only +def generate_trade_exit_signal_candles( + preprocessed_df: Dict[str, DataFrame], bt_results: Dict[str, Any] +) -> Dict[str, DataFrame]: + signal_candles_only = {} + for pair in preprocessed_df.keys(): + signal_candles_only_df = DataFrame() + + pairdf = preprocessed_df[pair] + resdf = bt_results["results"] + pairresults = resdf.loc[(resdf["pair"] == pair)] + + if pairdf.shape[0] > 0: + for t, v in pairresults.close_date.items(): + allinds = pairdf.loc[(pairdf["date"] < v)] + signal_inds = allinds.iloc[[-1]] + signal_candles_only_df = concat( + [signal_candles_only_df.infer_objects(), signal_inds.infer_objects()] + ) + + signal_candles_only[pair] = signal_candles_only_df + return signal_candles_only + + def generate_rejected_signals( preprocessed_df: Dict[str, DataFrame], rejected_dict: Dict[str, DataFrame] ) -> Dict[str, DataFrame]: diff --git a/tests/optimize/test_optimize_reports.py b/tests/optimize/test_optimize_reports.py index 4c7ce06e8da..60106d3bf22 100644 --- a/tests/optimize/test_optimize_reports.py +++ b/tests/optimize/test_optimize_reports.py @@ -293,7 +293,7 @@ def test_store_backtest_candles(testdatadir, mocker): candle_dict = {"DefStrat": {"UNITTEST/BTC": pd.DataFrame()}} # mock directory exporting - store_backtest_analysis_results(testdatadir, candle_dict, {}, "2022_01_01_15_05_13") + store_backtest_analysis_results(testdatadir, candle_dict, {}, {}, "2022_01_01_15_05_13") assert dump_mock.call_count == 2 assert isinstance(dump_mock.call_args_list[0][0][0], Path) @@ -302,7 +302,7 @@ def test_store_backtest_candles(testdatadir, mocker): dump_mock.reset_mock() # mock file exporting filename = Path(testdatadir / "testresult") - store_backtest_analysis_results(filename, candle_dict, {}, "2022_01_01_15_05_13") + store_backtest_analysis_results(filename, candle_dict, {}, {}, "2022_01_01_15_05_13") assert dump_mock.call_count == 2 assert isinstance(dump_mock.call_args_list[0][0][0], Path) # result will be testdatadir / testresult-_signals.pkl @@ -315,7 +315,7 @@ def test_write_read_backtest_candles(tmp_path): # test directory exporting sample_date = "2022_01_01_15_05_13" - store_backtest_analysis_results(tmp_path, candle_dict, {}, sample_date) + store_backtest_analysis_results(tmp_path, candle_dict, {}, {}, sample_date) stored_file = tmp_path / f"backtest-result-{sample_date}_signals.pkl" with stored_file.open("rb") as scp: pickled_signal_candles = joblib.load(scp) @@ -330,7 +330,7 @@ def test_write_read_backtest_candles(tmp_path): # test file exporting filename = tmp_path / "testresult" - store_backtest_analysis_results(filename, candle_dict, {}, sample_date) + store_backtest_analysis_results(filename, candle_dict, {}, {}, sample_date) stored_file = tmp_path / f"testresult-{sample_date}_signals.pkl" with stored_file.open("rb") as scp: pickled_signal_candles = joblib.load(scp) From b0e863dbbb46c5fd17524292e47e4a49395a4793 Mon Sep 17 00:00:00 2001 From: jainanuj94 Date: Fri, 2 Aug 2024 20:09:56 +0530 Subject: [PATCH 02/93] Introduce --exit-signals flag to backtesting-analysis command --- freqtrade/commands/arguments.py | 1 + freqtrade/commands/cli_options.py | 5 +++++ freqtrade/configuration/configuration.py | 1 + freqtrade/data/entryexitanalysis.py | 28 +++++------------------- tests/optimize/test_optimize_reports.py | 4 ++-- 5 files changed, 14 insertions(+), 25 deletions(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 0c93af78aaa..a7ecfb83c7b 100755 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -222,6 +222,7 @@ "indicator_list", "timerange", "analysis_rejected", + "analysis_exited", "analysis_to_csv", "analysis_csv_path", ] diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index b9236a0ab42..72df9b0c72f 100755 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -720,6 +720,11 @@ def __init__(self, *args, **kwargs): help="Analyse rejected signals", action="store_true", ), + "analysis_exited": Arg( + "--exit-signals", + help="Analyse indicators at exit signals", + action="store_true", + ), "analysis_to_csv": Arg( "--analysis-to-csv", help="Save selected analysis tables to individual CSVs", diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 184f9decf03..2e3eff74064 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -401,6 +401,7 @@ def _process_analyze_options(self, config: Config) -> None: ("indicator_list", "Analysis indicator list: {}"), ("timerange", "Filter trades by timerange: {}"), ("analysis_rejected", "Analyse rejected signals: {}"), + ("analysis_exited", "Analyse exited signals: {}"), ("analysis_to_csv", "Store analysis tables to CSV: {}"), ("analysis_csv_path", "Path to store analysis CSVs: {}"), # Lookahead analysis results diff --git a/freqtrade/data/entryexitanalysis.py b/freqtrade/data/entryexitanalysis.py index 7b18097df76..3b7f2e354bf 100644 --- a/freqtrade/data/entryexitanalysis.py +++ b/freqtrade/data/entryexitanalysis.py @@ -319,6 +319,7 @@ def process_entry_exit_reasons(config: Config): enter_reason_list = config.get("enter_reason_list", ["all"]) exit_reason_list = config.get("exit_reason_list", ["all"]) indicator_list = config.get("indicator_list", []) + do_exited = config.get("analysis_exited", False) do_rejected = config.get("analysis_rejected", False) to_csv = config.get("analysis_to_csv", False) csv_path = Path(config.get("analysis_csv_path", config["exportfilename"])) @@ -335,8 +336,10 @@ def process_entry_exit_reasons(config: Config): trades = load_backtest_data(config["exportfilename"], strategy_name) if trades is not None and not trades.empty: - signal_candles = _load_signal_candles(config["exportfilename"]) - exit_signal_candles = _load_exit_signal_candles(config["exportfilename"]) + if do_exited is True: + signal_candles = _load_exit_signal_candles(config["exportfilename"]) + else: + signal_candles = _load_signal_candles(config["exportfilename"]) rej_df = None if do_rejected: @@ -353,10 +356,6 @@ def process_entry_exit_reasons(config: Config): config["exchange"]["pair_whitelist"], strategy_name, trades, signal_candles ) - exited_trades_dict = _process_candles_and_indicators( - config["exchange"]["pair_whitelist"], strategy_name, trades, exit_signal_candles - ) - res_df = prepare_results( analysed_trades_dict, strategy_name, @@ -365,23 +364,6 @@ def process_entry_exit_reasons(config: Config): timerange=timerange, ) - exited_df = prepare_results( - exited_trades_dict, - strategy_name, - enter_reason_list, - exit_reason_list, - timerange=timerange, - ) - - print_results( - exited_df, - analysis_groups, - indicator_list, - to_csv=False, - rejected_signals=None, - csv_path=csv_path, - ) - print_results( res_df, analysis_groups, diff --git a/tests/optimize/test_optimize_reports.py b/tests/optimize/test_optimize_reports.py index 60106d3bf22..abbed1e54e2 100644 --- a/tests/optimize/test_optimize_reports.py +++ b/tests/optimize/test_optimize_reports.py @@ -295,7 +295,7 @@ def test_store_backtest_candles(testdatadir, mocker): # mock directory exporting store_backtest_analysis_results(testdatadir, candle_dict, {}, {}, "2022_01_01_15_05_13") - assert dump_mock.call_count == 2 + assert dump_mock.call_count == 3 assert isinstance(dump_mock.call_args_list[0][0][0], Path) assert str(dump_mock.call_args_list[0][0][0]).endswith("_signals.pkl") @@ -303,7 +303,7 @@ def test_store_backtest_candles(testdatadir, mocker): # mock file exporting filename = Path(testdatadir / "testresult") store_backtest_analysis_results(filename, candle_dict, {}, {}, "2022_01_01_15_05_13") - assert dump_mock.call_count == 2 + assert dump_mock.call_count == 3 assert isinstance(dump_mock.call_args_list[0][0][0], Path) # result will be testdatadir / testresult-_signals.pkl assert str(dump_mock.call_args_list[0][0][0]).endswith("_signals.pkl") From ecf9c173c44256541db09a86f31ee8ab0eb49134 Mon Sep 17 00:00:00 2001 From: jainanuj94 Date: Fri, 2 Aug 2024 20:46:19 +0530 Subject: [PATCH 03/93] Add test for backtesitng-analysis --- freqtrade/data/entryexitanalysis.py | 4 + tests/data/test_entryexitanalysis.py | 235 +++++++++++++++++++++++++++ 2 files changed, 239 insertions(+) diff --git a/freqtrade/data/entryexitanalysis.py b/freqtrade/data/entryexitanalysis.py index 3b7f2e354bf..d274eabe05c 100644 --- a/freqtrade/data/entryexitanalysis.py +++ b/freqtrade/data/entryexitanalysis.py @@ -260,8 +260,11 @@ def print_results( csv_path: Path, rejected_signals=None, to_csv=False, + exited_signals=False, ): if res_df.shape[0] > 0: + if exited_signals is True: + print("Analysing on exit signals.") if analysis_groups: _do_group_table_output(res_df, analysis_groups, to_csv=to_csv, csv_path=csv_path) @@ -371,6 +374,7 @@ def process_entry_exit_reasons(config: Config): rejected_signals=rej_df, to_csv=to_csv, csv_path=csv_path, + exited_signals=do_exited, ) except ValueError as e: diff --git a/tests/data/test_entryexitanalysis.py b/tests/data/test_entryexitanalysis.py index e7909c33967..1a00e22dd43 100644 --- a/tests/data/test_entryexitanalysis.py +++ b/tests/data/test_entryexitanalysis.py @@ -140,6 +140,7 @@ def test_backtest_analysis_nomock(default_conf, mocker, caplog, testdatadir, use ) start_analysis_entries_exits(args) captured = capsys.readouterr() + assert "Analysing on exit signals." not in captured.out assert "LTC/BTC" in captured.out assert "ETH/BTC" in captured.out assert "enter_tag_long_a" in captured.out @@ -245,3 +246,237 @@ def test_backtest_analysis_nomock(default_conf, mocker, caplog, testdatadir, use start_analysis_entries_exits(args) captured = capsys.readouterr() assert "no rejected signals" in captured.out + + +def test_backtest_analysis_on_exit_signals_nomock( + default_conf, mocker, caplog, testdatadir, user_dir, capsys +): + caplog.set_level(logging.INFO) + (user_dir / "backtest_results").mkdir(parents=True, exist_ok=True) + + default_conf.update( + { + "use_exit_signal": True, + "exit_profit_only": False, + "exit_profit_offset": 0.0, + "ignore_roi_if_entry_signal": False, + } + ) + patch_exchange(mocker) + result1 = pd.DataFrame( + { + "pair": ["ETH/BTC", "LTC/BTC", "ETH/BTC", "LTC/BTC"], + "profit_ratio": [0.025, 0.05, -0.1, -0.05], + "profit_abs": [0.5, 2.0, -4.0, -2.0], + "open_date": pd.to_datetime( + [ + "2018-01-29 18:40:00", + "2018-01-30 03:30:00", + "2018-01-30 08:10:00", + "2018-01-31 13:30:00", + ], + utc=True, + ), + "close_date": pd.to_datetime( + [ + "2018-01-29 20:45:00", + "2018-01-30 05:35:00", + "2018-01-30 09:10:00", + "2018-01-31 15:00:00", + ], + utc=True, + ), + "trade_duration": [235, 40, 60, 90], + "is_open": [False, False, False, False], + "stake_amount": [0.01, 0.01, 0.01, 0.01], + "open_rate": [0.104445, 0.10302485, 0.10302485, 0.10302485], + "close_rate": [0.104969, 0.103541, 0.102041, 0.102541], + "is_short": [False, False, False, False], + "enter_tag": [ + "enter_tag_long_a", + "enter_tag_long_b", + "enter_tag_long_a", + "enter_tag_long_b", + ], + "exit_reason": [ + ExitType.ROI.value, + ExitType.EXIT_SIGNAL.value, + ExitType.STOP_LOSS.value, + ExitType.TRAILING_STOP_LOSS.value, + ], + } + ) + + backtestmock = MagicMock( + side_effect=[ + { + "results": result1, + "config": default_conf, + "locks": [], + "rejected_signals": 20, + "timedout_entry_orders": 0, + "timedout_exit_orders": 0, + "canceled_trade_entries": 0, + "canceled_entry_orders": 0, + "replaced_entry_orders": 0, + "final_balance": 1000, + } + ] + ) + mocker.patch( + "freqtrade.plugins.pairlistmanager.PairListManager.whitelist", + PropertyMock(return_value=["ETH/BTC", "LTC/BTC", "DASH/BTC"]), + ) + mocker.patch("freqtrade.optimize.backtesting.Backtesting.backtest", backtestmock) + + patched_configuration_load_config_file(mocker, default_conf) + + args = [ + "backtesting", + "--config", + "config.json", + "--datadir", + str(testdatadir), + "--user-data-dir", + str(user_dir), + "--timeframe", + "5m", + "--timerange", + "1515560100-1517287800", + "--export", + "signals", + "--cache", + "none", + ] + args = get_args(args) + start_backtesting(args) + + captured = capsys.readouterr() + assert "BACKTESTING REPORT" in captured.out + assert "EXIT REASON STATS" in captured.out + assert "LEFT OPEN TRADES REPORT" in captured.out + + base_args = [ + "backtesting-analysis", + "--config", + "config.json", + "--datadir", + str(testdatadir), + "--user-data-dir", + str(user_dir), + ] + + # test group 0 and indicator list + args = get_args( + base_args + + [ + "--analysis-groups", + "0", + "--exit-signals", + "--indicator-list", + "close", + "rsi", + "profit_abs", + ] + ) + start_analysis_entries_exits(args) + captured = capsys.readouterr() + assert "Analysing on exit signals." in captured.out + assert "LTC/BTC" in captured.out + assert "ETH/BTC" in captured.out + assert "enter_tag_long_a" in captured.out + assert "enter_tag_long_b" in captured.out + assert "exit_signal" in captured.out + assert "roi" in captured.out + assert "stop_loss" in captured.out + assert "trailing_stop_loss" in captured.out + assert "0.5" in captured.out + assert "-4" in captured.out + assert "-2" in captured.out + assert "nan" in captured.out + assert "57.654" in captured.out + assert "0" in captured.out + assert "0.104" in captured.out + assert "0.016" in captured.out + assert "52.829" in captured.out + + # test group 1 + args = get_args(base_args + ["--analysis-groups", "1"]) + start_analysis_entries_exits(args) + captured = capsys.readouterr() + assert "enter_tag_long_a" in captured.out + assert "enter_tag_long_b" in captured.out + assert "total_profit_pct" in captured.out + assert "-3.5" in captured.out + assert "-1.75" in captured.out + assert "-7.5" in captured.out + assert "-3.75" in captured.out + assert "0" in captured.out + + # test group 2 + args = get_args(base_args + ["--analysis-groups", "2"]) + start_analysis_entries_exits(args) + captured = capsys.readouterr() + assert "enter_tag_long_a" in captured.out + assert "enter_tag_long_b" in captured.out + assert "exit_signal" in captured.out + assert "roi" in captured.out + assert "stop_loss" in captured.out + assert "trailing_stop_loss" in captured.out + assert "total_profit_pct" in captured.out + assert "-10" in captured.out + assert "-5" in captured.out + assert "2.5" in captured.out + + # test group 3 + args = get_args(base_args + ["--analysis-groups", "3"]) + start_analysis_entries_exits(args) + captured = capsys.readouterr() + assert "LTC/BTC" in captured.out + assert "ETH/BTC" in captured.out + assert "enter_tag_long_a" in captured.out + assert "enter_tag_long_b" in captured.out + assert "total_profit_pct" in captured.out + assert "-7.5" in captured.out + assert "-3.75" in captured.out + assert "-1.75" in captured.out + assert "0" in captured.out + assert "2" in captured.out + + # test group 4 + args = get_args(base_args + ["--analysis-groups", "4"]) + start_analysis_entries_exits(args) + captured = capsys.readouterr() + assert "LTC/BTC" in captured.out + assert "ETH/BTC" in captured.out + assert "enter_tag_long_a" in captured.out + assert "enter_tag_long_b" in captured.out + assert "exit_signal" in captured.out + assert "roi" in captured.out + assert "stop_loss" in captured.out + assert "trailing_stop_loss" in captured.out + assert "total_profit_pct" in captured.out + assert "-10" in captured.out + assert "-5" in captured.out + assert "-4" in captured.out + assert "0.5" in captured.out + assert "1" in captured.out + assert "2.5" in captured.out + + # test group 5 + args = get_args(base_args + ["--analysis-groups", "5"]) + start_analysis_entries_exits(args) + captured = capsys.readouterr() + assert "exit_signal" in captured.out + assert "roi" in captured.out + assert "stop_loss" in captured.out + assert "trailing_stop_loss" in captured.out + + # test date filtering + args = get_args( + base_args + ["--analysis-groups", "0", "1", "2", "--timerange", "20180129-20180130"] + ) + start_analysis_entries_exits(args) + captured = capsys.readouterr() + assert "enter_tag_long_a" in captured.out + assert "enter_tag_long_b" not in captured.out From 7f0e5dd3350167a838f62c5459f62070eead3fcb Mon Sep 17 00:00:00 2001 From: jainanuj94 Date: Mon, 5 Aug 2024 23:19:38 +0530 Subject: [PATCH 04/93] Refactor and add documentation --- docs/advanced-backtesting.md | 18 ++++++++--- freqtrade/data/entryexitanalysis.py | 22 +++++++++---- freqtrade/optimize/backtesting.py | 11 +++---- .../optimize/optimize_reports/__init__.py | 3 +- .../optimize_reports/optimize_reports.py | 31 +++---------------- tests/data/test_entryexitanalysis.py | 2 +- 6 files changed, 40 insertions(+), 47 deletions(-) diff --git a/docs/advanced-backtesting.md b/docs/advanced-backtesting.md index 563e5df08b8..c01d763f8c6 100644 --- a/docs/advanced-backtesting.md +++ b/docs/advanced-backtesting.md @@ -18,15 +18,15 @@ freqtrade backtesting -c --timeframe --strategy --rejected-signals ``` +### Printing analysis on exit signals + +Use the `--exit-signals` option to print out analysis on exited signals. + +```bash +freqtrade backtesting-analysis -c --exit-signals +``` + ### Writing tables to CSV Some of the tabular outputs can become large, so printing them out to the terminal is not preferable. diff --git a/freqtrade/data/entryexitanalysis.py b/freqtrade/data/entryexitanalysis.py index d274eabe05c..f2440a787ec 100644 --- a/freqtrade/data/entryexitanalysis.py +++ b/freqtrade/data/entryexitanalysis.py @@ -51,7 +51,9 @@ def _load_exit_signal_candles(backtest_dir: Path): return _load_backtest_analysis_data(backtest_dir, "exited") -def _process_candles_and_indicators(pairlist, strategy_name, trades, signal_candles): +def _process_candles_and_indicators( + pairlist, strategy_name, trades, signal_candles, analyse_on="open_date" +): analysed_trades_dict = {strategy_name: {}} try: @@ -60,7 +62,7 @@ def _process_candles_and_indicators(pairlist, strategy_name, trades, signal_cand for pair in pairlist: if pair in signal_candles[strategy_name]: analysed_trades_dict[strategy_name][pair] = _analyze_candles_and_indicators( - pair, trades, signal_candles[strategy_name][pair] + pair, trades, signal_candles[strategy_name][pair], analyse_on ) except Exception as e: print(f"Cannot process entry/exit reasons for {strategy_name}: ", e) @@ -68,7 +70,9 @@ def _process_candles_and_indicators(pairlist, strategy_name, trades, signal_cand return analysed_trades_dict -def _analyze_candles_and_indicators(pair, trades: pd.DataFrame, signal_candles: pd.DataFrame): +def _analyze_candles_and_indicators( + pair, trades: pd.DataFrame, signal_candles: pd.DataFrame, analyse_on="open_date" +): buyf = signal_candles if len(buyf) > 0: @@ -78,8 +82,8 @@ def _analyze_candles_and_indicators(pair, trades: pd.DataFrame, signal_candles: trades_inds = pd.DataFrame() if trades_red.shape[0] > 0 and buyf.shape[0] > 0: - for t, v in trades_red.open_date.items(): - allinds = buyf.loc[(buyf["date"] < v)] + for t, v in trades_red.iterrows(): + allinds = buyf.loc[(buyf["date"] < v[analyse_on])] if allinds.shape[0] > 0: tmp_inds = allinds.iloc[[-1]] @@ -339,8 +343,10 @@ def process_entry_exit_reasons(config: Config): trades = load_backtest_data(config["exportfilename"], strategy_name) if trades is not None and not trades.empty: + analyse_on = "open_date" if do_exited is True: signal_candles = _load_exit_signal_candles(config["exportfilename"]) + analyse_on = "close_date" else: signal_candles = _load_signal_candles(config["exportfilename"]) @@ -356,7 +362,11 @@ def process_entry_exit_reasons(config: Config): ) analysed_trades_dict = _process_candles_and_indicators( - config["exchange"]["pair_whitelist"], strategy_name, trades, signal_candles + config["exchange"]["pair_whitelist"], + strategy_name, + trades, + signal_candles, + analyse_on, ) res_df = prepare_results( diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 8beb05ff4f7..bb088d064a6 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -42,8 +42,7 @@ from freqtrade.optimize.optimize_reports import ( generate_backtest_stats, generate_rejected_signals, - generate_trade_entry_signal_candles, - generate_trade_exit_signal_candles, + generate_trade_signal_candles, show_backtest_results, store_backtest_analysis_results, store_backtest_stats, @@ -1560,14 +1559,14 @@ def backtest_one_strategy( self.config.get("export", "none") == "signals" and self.dataprovider.runmode == RunMode.BACKTEST ): - self.processed_dfs[strategy_name] = generate_trade_entry_signal_candles( - preprocessed_tmp, results + self.processed_dfs[strategy_name] = generate_trade_signal_candles( + preprocessed_tmp, results, "open_date" ) self.rejected_df[strategy_name] = generate_rejected_signals( preprocessed_tmp, self.rejected_dict ) - self.exited_dfs[strategy_name] = generate_trade_exit_signal_candles( - preprocessed_tmp, results + self.exited_dfs[strategy_name] = generate_trade_signal_candles( + preprocessed_tmp, results, "close_date" ) return min_date, max_date diff --git a/freqtrade/optimize/optimize_reports/__init__.py b/freqtrade/optimize/optimize_reports/__init__.py index 1dcc1e885b4..6f3278a1c7e 100644 --- a/freqtrade/optimize/optimize_reports/__init__.py +++ b/freqtrade/optimize/optimize_reports/__init__.py @@ -25,7 +25,6 @@ generate_strategy_comparison, generate_strategy_stats, generate_tag_metrics, - generate_trade_entry_signal_candles, - generate_trade_exit_signal_candles, + generate_trade_signal_candles, generate_trading_stats, ) diff --git a/freqtrade/optimize/optimize_reports/optimize_reports.py b/freqtrade/optimize/optimize_reports/optimize_reports.py index e82184516f3..2b0fd54bf7c 100644 --- a/freqtrade/optimize/optimize_reports/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports/optimize_reports.py @@ -24,8 +24,8 @@ logger = logging.getLogger(__name__) -def generate_trade_entry_signal_candles( - preprocessed_df: Dict[str, DataFrame], bt_results: Dict[str, Any] +def generate_trade_signal_candles( + preprocessed_df: Dict[str, DataFrame], bt_results: Dict[str, Any], analysis_on="open_date" ) -> Dict[str, DataFrame]: signal_candles_only = {} for pair in preprocessed_df.keys(): @@ -36,31 +36,8 @@ def generate_trade_entry_signal_candles( pairresults = resdf.loc[(resdf["pair"] == pair)] if pairdf.shape[0] > 0: - for t, v in pairresults.open_date.items(): - allinds = pairdf.loc[(pairdf["date"] < v)] - signal_inds = allinds.iloc[[-1]] - signal_candles_only_df = concat( - [signal_candles_only_df.infer_objects(), signal_inds.infer_objects()] - ) - - signal_candles_only[pair] = signal_candles_only_df - return signal_candles_only - - -def generate_trade_exit_signal_candles( - preprocessed_df: Dict[str, DataFrame], bt_results: Dict[str, Any] -) -> Dict[str, DataFrame]: - signal_candles_only = {} - for pair in preprocessed_df.keys(): - signal_candles_only_df = DataFrame() - - pairdf = preprocessed_df[pair] - resdf = bt_results["results"] - pairresults = resdf.loc[(resdf["pair"] == pair)] - - if pairdf.shape[0] > 0: - for t, v in pairresults.close_date.items(): - allinds = pairdf.loc[(pairdf["date"] < v)] + for t, v in pairresults.iterrows(): + allinds = pairdf.loc[(pairdf["date"] < v[analysis_on])] signal_inds = allinds.iloc[[-1]] signal_candles_only_df = concat( [signal_candles_only_df.infer_objects(), signal_inds.infer_objects()] diff --git a/tests/data/test_entryexitanalysis.py b/tests/data/test_entryexitanalysis.py index 1a00e22dd43..66e0179f8d0 100644 --- a/tests/data/test_entryexitanalysis.py +++ b/tests/data/test_entryexitanalysis.py @@ -393,8 +393,8 @@ def test_backtest_analysis_on_exit_signals_nomock( assert "0.5" in captured.out assert "-4" in captured.out assert "-2" in captured.out - assert "nan" in captured.out assert "57.654" in captured.out + assert "44.428" in captured.out assert "0" in captured.out assert "0.104" in captured.out assert "0.016" in captured.out From 103991746bfe29725a1634f1157c9b344e0b23a7 Mon Sep 17 00:00:00 2001 From: jainanuj94 Date: Mon, 5 Aug 2024 23:57:24 +0530 Subject: [PATCH 05/93] chore: type safety and refactoring --- freqtrade/data/entryexitanalysis.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/freqtrade/data/entryexitanalysis.py b/freqtrade/data/entryexitanalysis.py index f2440a787ec..99b0e3bcb40 100644 --- a/freqtrade/data/entryexitanalysis.py +++ b/freqtrade/data/entryexitanalysis.py @@ -1,6 +1,6 @@ import logging from pathlib import Path -from typing import List +from typing import Dict, List import joblib import pandas as pd @@ -47,7 +47,7 @@ def _load_signal_candles(backtest_dir: Path): return _load_backtest_analysis_data(backtest_dir, "signals") -def _load_exit_signal_candles(backtest_dir: Path): +def _load_exit_signal_candles(backtest_dir: Path) -> Dict[str, Dict[str, pd.DataFrame]]: return _load_backtest_analysis_data(backtest_dir, "exited") @@ -71,8 +71,8 @@ def _process_candles_and_indicators( def _analyze_candles_and_indicators( - pair, trades: pd.DataFrame, signal_candles: pd.DataFrame, analyse_on="open_date" -): + pair: str, trades: pd.DataFrame, signal_candles: pd.DataFrame, analyse_on="open_date" +) -> pd.DataFrame: buyf = signal_candles if len(buyf) > 0: @@ -242,7 +242,7 @@ def _select_rows_by_tags(df, enter_reason_list, exit_reason_list): def prepare_results( analysed_trades, stratname, enter_reason_list, exit_reason_list, timerange=None -): +) -> pd.DataFrame: res_df = pd.DataFrame() for pair, trades in analysed_trades[stratname].items(): if trades.shape[0] > 0: From 3ebc5b136c98e37abd7883171fea54c502730ce8 Mon Sep 17 00:00:00 2001 From: jainanuj94 Date: Tue, 6 Aug 2024 12:55:48 +0530 Subject: [PATCH 06/93] review comments and update test for exit signals --- docs/advanced-backtesting.md | 2 +- .../optimize/optimize_reports/optimize_reports.py | 2 +- tests/data/test_entryexitanalysis.py | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/advanced-backtesting.md b/docs/advanced-backtesting.md index c01d763f8c6..627ff20cd31 100644 --- a/docs/advanced-backtesting.md +++ b/docs/advanced-backtesting.md @@ -18,7 +18,7 @@ freqtrade backtesting -c --timeframe --strategy Dict[str, DataFrame]: signal_candles_only = {} for pair in preprocessed_df.keys(): diff --git a/tests/data/test_entryexitanalysis.py b/tests/data/test_entryexitanalysis.py index 66e0179f8d0..b55661baba9 100644 --- a/tests/data/test_entryexitanalysis.py +++ b/tests/data/test_entryexitanalysis.py @@ -18,7 +18,9 @@ def entryexitanalysis_cleanup() -> None: Backtesting.cleanup() -def test_backtest_analysis_nomock(default_conf, mocker, caplog, testdatadir, user_dir, capsys): +def test_backtest_analysis_on_entry_and_rejected_signals_nomock( + default_conf, mocker, caplog, testdatadir, user_dir, capsys +): caplog.set_level(logging.INFO) (user_dir / "backtest_results").mkdir(parents=True, exist_ok=True) @@ -140,7 +142,6 @@ def test_backtest_analysis_nomock(default_conf, mocker, caplog, testdatadir, use ) start_analysis_entries_exits(args) captured = capsys.readouterr() - assert "Analysing on exit signals." not in captured.out assert "LTC/BTC" in captured.out assert "ETH/BTC" in captured.out assert "enter_tag_long_a" in captured.out @@ -279,7 +280,7 @@ def test_backtest_analysis_on_exit_signals_nomock( ), "close_date": pd.to_datetime( [ - "2018-01-29 20:45:00", + "2018-01-30 20:45:00", "2018-01-30 05:35:00", "2018-01-30 09:10:00", "2018-01-31 15:00:00", @@ -381,7 +382,6 @@ def test_backtest_analysis_on_exit_signals_nomock( ) start_analysis_entries_exits(args) captured = capsys.readouterr() - assert "Analysing on exit signals." in captured.out assert "LTC/BTC" in captured.out assert "ETH/BTC" in captured.out assert "enter_tag_long_a" in captured.out @@ -394,7 +394,7 @@ def test_backtest_analysis_on_exit_signals_nomock( assert "-4" in captured.out assert "-2" in captured.out assert "57.654" in captured.out - assert "44.428" in captured.out + assert "-8" in captured.out assert "0" in captured.out assert "0.104" in captured.out assert "0.016" in captured.out From d351ed0173eae322f1d2ed011c91ce0debe1f83f Mon Sep 17 00:00:00 2001 From: jainanuj94 Date: Tue, 6 Aug 2024 15:16:30 +0530 Subject: [PATCH 07/93] refactor: change analyse_on variable name to date_col --- freqtrade/data/entryexitanalysis.py | 18 ++++++++---------- .../optimize_reports/optimize_reports.py | 4 ++-- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/freqtrade/data/entryexitanalysis.py b/freqtrade/data/entryexitanalysis.py index 99b0e3bcb40..4e64c1eccbc 100644 --- a/freqtrade/data/entryexitanalysis.py +++ b/freqtrade/data/entryexitanalysis.py @@ -51,10 +51,8 @@ def _load_exit_signal_candles(backtest_dir: Path) -> Dict[str, Dict[str, pd.Data return _load_backtest_analysis_data(backtest_dir, "exited") -def _process_candles_and_indicators( - pairlist, strategy_name, trades, signal_candles, analyse_on="open_date" -): - analysed_trades_dict = {strategy_name: {}} +def _process_candles_and_indicators(pairlist, strategy_name, trades, signal_candles, date_col: str): + analysed_trades_dict: Dict[str, Dict] = {strategy_name: {}} try: logger.info(f"Processing {strategy_name} : {len(pairlist)} pairs") @@ -62,7 +60,7 @@ def _process_candles_and_indicators( for pair in pairlist: if pair in signal_candles[strategy_name]: analysed_trades_dict[strategy_name][pair] = _analyze_candles_and_indicators( - pair, trades, signal_candles[strategy_name][pair], analyse_on + pair, trades, signal_candles[strategy_name][pair], date_col ) except Exception as e: print(f"Cannot process entry/exit reasons for {strategy_name}: ", e) @@ -71,7 +69,7 @@ def _process_candles_and_indicators( def _analyze_candles_and_indicators( - pair: str, trades: pd.DataFrame, signal_candles: pd.DataFrame, analyse_on="open_date" + pair: str, trades: pd.DataFrame, signal_candles: pd.DataFrame, date_col: str ) -> pd.DataFrame: buyf = signal_candles @@ -83,7 +81,7 @@ def _analyze_candles_and_indicators( if trades_red.shape[0] > 0 and buyf.shape[0] > 0: for t, v in trades_red.iterrows(): - allinds = buyf.loc[(buyf["date"] < v[analyse_on])] + allinds = buyf.loc[(buyf["date"] < v[date_col])] if allinds.shape[0] > 0: tmp_inds = allinds.iloc[[-1]] @@ -343,10 +341,10 @@ def process_entry_exit_reasons(config: Config): trades = load_backtest_data(config["exportfilename"], strategy_name) if trades is not None and not trades.empty: - analyse_on = "open_date" + date_col = "open_date" if do_exited is True: signal_candles = _load_exit_signal_candles(config["exportfilename"]) - analyse_on = "close_date" + date_col = "close_date" else: signal_candles = _load_signal_candles(config["exportfilename"]) @@ -366,7 +364,7 @@ def process_entry_exit_reasons(config: Config): strategy_name, trades, signal_candles, - analyse_on, + date_col, ) res_df = prepare_results( diff --git a/freqtrade/optimize/optimize_reports/optimize_reports.py b/freqtrade/optimize/optimize_reports/optimize_reports.py index f90bba3f900..24c2d049fbc 100644 --- a/freqtrade/optimize/optimize_reports/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports/optimize_reports.py @@ -25,7 +25,7 @@ def generate_trade_signal_candles( - preprocessed_df: Dict[str, DataFrame], bt_results: Dict[str, Any], analysis_on: str + preprocessed_df: Dict[str, DataFrame], bt_results: Dict[str, Any], date_col: str ) -> Dict[str, DataFrame]: signal_candles_only = {} for pair in preprocessed_df.keys(): @@ -37,7 +37,7 @@ def generate_trade_signal_candles( if pairdf.shape[0] > 0: for t, v in pairresults.iterrows(): - allinds = pairdf.loc[(pairdf["date"] < v[analysis_on])] + allinds = pairdf.loc[(pairdf["date"] < v[date_col])] signal_inds = allinds.iloc[[-1]] signal_candles_only_df = concat( [signal_candles_only_df.infer_objects(), signal_inds.infer_objects()] From 8085e24dcde19744e5b8e42ff9ce8212a21c2d88 Mon Sep 17 00:00:00 2001 From: jainanuj94 Date: Tue, 6 Aug 2024 20:00:05 +0530 Subject: [PATCH 08/93] update tests --- tests/optimize/test_optimize_reports.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/optimize/test_optimize_reports.py b/tests/optimize/test_optimize_reports.py index abbed1e54e2..40673a1b66b 100644 --- a/tests/optimize/test_optimize_reports.py +++ b/tests/optimize/test_optimize_reports.py @@ -298,6 +298,8 @@ def test_store_backtest_candles(testdatadir, mocker): assert dump_mock.call_count == 3 assert isinstance(dump_mock.call_args_list[0][0][0], Path) assert str(dump_mock.call_args_list[0][0][0]).endswith("_signals.pkl") + assert str(dump_mock.call_args_list[1][0][0]).endswith("_rejected.pkl") + assert str(dump_mock.call_args_list[2][0][0]).endswith("_exited.pkl") dump_mock.reset_mock() # mock file exporting @@ -307,6 +309,9 @@ def test_store_backtest_candles(testdatadir, mocker): assert isinstance(dump_mock.call_args_list[0][0][0], Path) # result will be testdatadir / testresult-_signals.pkl assert str(dump_mock.call_args_list[0][0][0]).endswith("_signals.pkl") + assert str(dump_mock.call_args_list[1][0][0]).endswith("_rejected.pkl") + assert str(dump_mock.call_args_list[2][0][0]).endswith("_exited.pkl") + dump_mock.reset_mock() From 19a2e06c0bc31beff77571ffb9f56686f0a54c0f Mon Sep 17 00:00:00 2001 From: jainanuj94 Date: Sun, 18 Aug 2024 18:41:04 +0530 Subject: [PATCH 09/93] #000 | Anuj | Merge Dfs for entry and exit in one table --- freqtrade/data/entryexitanalysis.py | 85 +++++++--- tests/data/test_entryexitanalysis.py | 241 +-------------------------- 2 files changed, 72 insertions(+), 254 deletions(-) diff --git a/freqtrade/data/entryexitanalysis.py b/freqtrade/data/entryexitanalysis.py index 4e64c1eccbc..e217294a25b 100644 --- a/freqtrade/data/entryexitanalysis.py +++ b/freqtrade/data/entryexitanalysis.py @@ -257,16 +257,14 @@ def prepare_results( def print_results( res_df: pd.DataFrame, + exit_df: pd.DataFrame, analysis_groups: List[str], indicator_list: List[str], csv_path: Path, rejected_signals=None, to_csv=False, - exited_signals=False, ): if res_df.shape[0] > 0: - if exited_signals is True: - print("Analysing on exit signals.") if analysis_groups: _do_group_table_output(res_df, analysis_groups, to_csv=to_csv, csv_path=csv_path) @@ -286,9 +284,11 @@ def print_results( for ind in indicator_list: if ind in res_df: available_inds.append(ind) - ilist = ["pair", "enter_reason", "exit_reason"] + available_inds + + merged_df = _merge_dfs(res_df, exit_df, available_inds) + _print_table( - res_df[ilist], + merged_df, sortcols=["exit_reason"], show_index=False, name="Indicators:", @@ -299,6 +299,21 @@ def print_results( print("\\No trades to show") +def _merge_dfs(entry_df, exit_df, available_inds): + merge_on = ["pair", "open_date"] + columns_to_keep = merge_on + ["enter_reason", "exit_reason"] + available_inds + if exit_df is not None and not exit_df.empty: + merged_df = pd.merge( + entry_df[columns_to_keep], + exit_df[merge_on + available_inds], + on=merge_on, + suffixes=(" (entry)", " (exit)"), + ) + else: + merged_df = entry_df[columns_to_keep] + return merged_df + + def _print_table( df: pd.DataFrame, sortcols=None, *, show_index=False, name=None, to_csv=False, csv_path: Path ): @@ -324,7 +339,6 @@ def process_entry_exit_reasons(config: Config): enter_reason_list = config.get("enter_reason_list", ["all"]) exit_reason_list = config.get("exit_reason_list", ["all"]) indicator_list = config.get("indicator_list", []) - do_exited = config.get("analysis_exited", False) do_rejected = config.get("analysis_rejected", False) to_csv = config.get("analysis_to_csv", False) csv_path = Path(config.get("analysis_csv_path", config["exportfilename"])) @@ -341,12 +355,8 @@ def process_entry_exit_reasons(config: Config): trades = load_backtest_data(config["exportfilename"], strategy_name) if trades is not None and not trades.empty: - date_col = "open_date" - if do_exited is True: - signal_candles = _load_exit_signal_candles(config["exportfilename"]) - date_col = "close_date" - else: - signal_candles = _load_signal_candles(config["exportfilename"]) + signal_candles = _load_signal_candles(config["exportfilename"]) + exit_signals = _load_exit_signal_candles(config["exportfilename"]) rej_df = None if do_rejected: @@ -359,31 +369,64 @@ def process_entry_exit_reasons(config: Config): timerange=timerange, ) - analysed_trades_dict = _process_candles_and_indicators( + entry_df = _generate_dfs( config["exchange"]["pair_whitelist"], + enter_reason_list, + exit_reason_list, + signal_candles, strategy_name, + timerange, trades, - signal_candles, - date_col, + "open_date", ) - res_df = prepare_results( - analysed_trades_dict, - strategy_name, + exit_df = _generate_dfs( + config["exchange"]["pair_whitelist"], enter_reason_list, exit_reason_list, - timerange=timerange, + exit_signals, + strategy_name, + timerange, + trades, + "close_date", ) print_results( - res_df, + entry_df, + exit_df, analysis_groups, indicator_list, rejected_signals=rej_df, to_csv=to_csv, csv_path=csv_path, - exited_signals=do_exited, ) except ValueError as e: raise OperationalException(e) from e + + +def _generate_dfs( + pairlist: list, + enter_reason_list: list, + exit_reason_list: list, + signal_candles: Dict, + strategy_name: str, + timerange: TimeRange, + trades: pd.DataFrame, + date_col: str, +) -> pd.DataFrame: + analysed_trades_dict = _process_candles_and_indicators( + pairlist, + strategy_name, + trades, + signal_candles, + date_col, + ) + res_df = prepare_results( + analysed_trades_dict, + strategy_name, + enter_reason_list, + exit_reason_list, + timerange=timerange, + ) + return res_df diff --git a/tests/data/test_entryexitanalysis.py b/tests/data/test_entryexitanalysis.py index b55661baba9..39456b7a389 100644 --- a/tests/data/test_entryexitanalysis.py +++ b/tests/data/test_entryexitanalysis.py @@ -160,6 +160,14 @@ def test_backtest_analysis_on_entry_and_rejected_signals_nomock( assert "34.049" in captured.out assert "0.104" in captured.out assert "52.829" in captured.out + # assert indicator list + assert "close (entry)" in captured.out + assert "0.016" in captured.out + assert "rsi (entry)" in captured.out + assert "54.320" in captured.out + assert "close (exit)" in captured.out + assert "rsi (exit)" in captured.out + assert "52.829" in captured.out # test group 1 args = get_args(base_args + ["--analysis-groups", "1"]) @@ -247,236 +255,3 @@ def test_backtest_analysis_on_entry_and_rejected_signals_nomock( start_analysis_entries_exits(args) captured = capsys.readouterr() assert "no rejected signals" in captured.out - - -def test_backtest_analysis_on_exit_signals_nomock( - default_conf, mocker, caplog, testdatadir, user_dir, capsys -): - caplog.set_level(logging.INFO) - (user_dir / "backtest_results").mkdir(parents=True, exist_ok=True) - - default_conf.update( - { - "use_exit_signal": True, - "exit_profit_only": False, - "exit_profit_offset": 0.0, - "ignore_roi_if_entry_signal": False, - } - ) - patch_exchange(mocker) - result1 = pd.DataFrame( - { - "pair": ["ETH/BTC", "LTC/BTC", "ETH/BTC", "LTC/BTC"], - "profit_ratio": [0.025, 0.05, -0.1, -0.05], - "profit_abs": [0.5, 2.0, -4.0, -2.0], - "open_date": pd.to_datetime( - [ - "2018-01-29 18:40:00", - "2018-01-30 03:30:00", - "2018-01-30 08:10:00", - "2018-01-31 13:30:00", - ], - utc=True, - ), - "close_date": pd.to_datetime( - [ - "2018-01-30 20:45:00", - "2018-01-30 05:35:00", - "2018-01-30 09:10:00", - "2018-01-31 15:00:00", - ], - utc=True, - ), - "trade_duration": [235, 40, 60, 90], - "is_open": [False, False, False, False], - "stake_amount": [0.01, 0.01, 0.01, 0.01], - "open_rate": [0.104445, 0.10302485, 0.10302485, 0.10302485], - "close_rate": [0.104969, 0.103541, 0.102041, 0.102541], - "is_short": [False, False, False, False], - "enter_tag": [ - "enter_tag_long_a", - "enter_tag_long_b", - "enter_tag_long_a", - "enter_tag_long_b", - ], - "exit_reason": [ - ExitType.ROI.value, - ExitType.EXIT_SIGNAL.value, - ExitType.STOP_LOSS.value, - ExitType.TRAILING_STOP_LOSS.value, - ], - } - ) - - backtestmock = MagicMock( - side_effect=[ - { - "results": result1, - "config": default_conf, - "locks": [], - "rejected_signals": 20, - "timedout_entry_orders": 0, - "timedout_exit_orders": 0, - "canceled_trade_entries": 0, - "canceled_entry_orders": 0, - "replaced_entry_orders": 0, - "final_balance": 1000, - } - ] - ) - mocker.patch( - "freqtrade.plugins.pairlistmanager.PairListManager.whitelist", - PropertyMock(return_value=["ETH/BTC", "LTC/BTC", "DASH/BTC"]), - ) - mocker.patch("freqtrade.optimize.backtesting.Backtesting.backtest", backtestmock) - - patched_configuration_load_config_file(mocker, default_conf) - - args = [ - "backtesting", - "--config", - "config.json", - "--datadir", - str(testdatadir), - "--user-data-dir", - str(user_dir), - "--timeframe", - "5m", - "--timerange", - "1515560100-1517287800", - "--export", - "signals", - "--cache", - "none", - ] - args = get_args(args) - start_backtesting(args) - - captured = capsys.readouterr() - assert "BACKTESTING REPORT" in captured.out - assert "EXIT REASON STATS" in captured.out - assert "LEFT OPEN TRADES REPORT" in captured.out - - base_args = [ - "backtesting-analysis", - "--config", - "config.json", - "--datadir", - str(testdatadir), - "--user-data-dir", - str(user_dir), - ] - - # test group 0 and indicator list - args = get_args( - base_args - + [ - "--analysis-groups", - "0", - "--exit-signals", - "--indicator-list", - "close", - "rsi", - "profit_abs", - ] - ) - start_analysis_entries_exits(args) - captured = capsys.readouterr() - assert "LTC/BTC" in captured.out - assert "ETH/BTC" in captured.out - assert "enter_tag_long_a" in captured.out - assert "enter_tag_long_b" in captured.out - assert "exit_signal" in captured.out - assert "roi" in captured.out - assert "stop_loss" in captured.out - assert "trailing_stop_loss" in captured.out - assert "0.5" in captured.out - assert "-4" in captured.out - assert "-2" in captured.out - assert "57.654" in captured.out - assert "-8" in captured.out - assert "0" in captured.out - assert "0.104" in captured.out - assert "0.016" in captured.out - assert "52.829" in captured.out - - # test group 1 - args = get_args(base_args + ["--analysis-groups", "1"]) - start_analysis_entries_exits(args) - captured = capsys.readouterr() - assert "enter_tag_long_a" in captured.out - assert "enter_tag_long_b" in captured.out - assert "total_profit_pct" in captured.out - assert "-3.5" in captured.out - assert "-1.75" in captured.out - assert "-7.5" in captured.out - assert "-3.75" in captured.out - assert "0" in captured.out - - # test group 2 - args = get_args(base_args + ["--analysis-groups", "2"]) - start_analysis_entries_exits(args) - captured = capsys.readouterr() - assert "enter_tag_long_a" in captured.out - assert "enter_tag_long_b" in captured.out - assert "exit_signal" in captured.out - assert "roi" in captured.out - assert "stop_loss" in captured.out - assert "trailing_stop_loss" in captured.out - assert "total_profit_pct" in captured.out - assert "-10" in captured.out - assert "-5" in captured.out - assert "2.5" in captured.out - - # test group 3 - args = get_args(base_args + ["--analysis-groups", "3"]) - start_analysis_entries_exits(args) - captured = capsys.readouterr() - assert "LTC/BTC" in captured.out - assert "ETH/BTC" in captured.out - assert "enter_tag_long_a" in captured.out - assert "enter_tag_long_b" in captured.out - assert "total_profit_pct" in captured.out - assert "-7.5" in captured.out - assert "-3.75" in captured.out - assert "-1.75" in captured.out - assert "0" in captured.out - assert "2" in captured.out - - # test group 4 - args = get_args(base_args + ["--analysis-groups", "4"]) - start_analysis_entries_exits(args) - captured = capsys.readouterr() - assert "LTC/BTC" in captured.out - assert "ETH/BTC" in captured.out - assert "enter_tag_long_a" in captured.out - assert "enter_tag_long_b" in captured.out - assert "exit_signal" in captured.out - assert "roi" in captured.out - assert "stop_loss" in captured.out - assert "trailing_stop_loss" in captured.out - assert "total_profit_pct" in captured.out - assert "-10" in captured.out - assert "-5" in captured.out - assert "-4" in captured.out - assert "0.5" in captured.out - assert "1" in captured.out - assert "2.5" in captured.out - - # test group 5 - args = get_args(base_args + ["--analysis-groups", "5"]) - start_analysis_entries_exits(args) - captured = capsys.readouterr() - assert "exit_signal" in captured.out - assert "roi" in captured.out - assert "stop_loss" in captured.out - assert "trailing_stop_loss" in captured.out - - # test date filtering - args = get_args( - base_args + ["--analysis-groups", "0", "1", "2", "--timerange", "20180129-20180130"] - ) - start_analysis_entries_exits(args) - captured = capsys.readouterr() - assert "enter_tag_long_a" in captured.out - assert "enter_tag_long_b" not in captured.out From c3679910a43ee034e752dce01391e04d9d448d60 Mon Sep 17 00:00:00 2001 From: jainanuj94 Date: Sun, 18 Aug 2024 23:14:21 +0530 Subject: [PATCH 10/93] remove additional argument --- freqtrade/commands/arguments.py | 1 - freqtrade/commands/cli_options.py | 5 ----- freqtrade/configuration/configuration.py | 1 - 3 files changed, 7 deletions(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index a7ecfb83c7b..0c93af78aaa 100755 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -222,7 +222,6 @@ "indicator_list", "timerange", "analysis_rejected", - "analysis_exited", "analysis_to_csv", "analysis_csv_path", ] diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 72df9b0c72f..b9236a0ab42 100755 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -720,11 +720,6 @@ def __init__(self, *args, **kwargs): help="Analyse rejected signals", action="store_true", ), - "analysis_exited": Arg( - "--exit-signals", - help="Analyse indicators at exit signals", - action="store_true", - ), "analysis_to_csv": Arg( "--analysis-to-csv", help="Save selected analysis tables to individual CSVs", diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 2e3eff74064..184f9decf03 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -401,7 +401,6 @@ def _process_analyze_options(self, config: Config) -> None: ("indicator_list", "Analysis indicator list: {}"), ("timerange", "Filter trades by timerange: {}"), ("analysis_rejected", "Analyse rejected signals: {}"), - ("analysis_exited", "Analyse exited signals: {}"), ("analysis_to_csv", "Store analysis tables to CSV: {}"), ("analysis_csv_path", "Path to store analysis CSVs: {}"), # Lookahead analysis results From b6702d1d322a6c22d59c0fe2c319bfe7347d306f Mon Sep 17 00:00:00 2001 From: jainanuj94 Date: Sun, 18 Aug 2024 23:22:20 +0530 Subject: [PATCH 11/93] simplify merging logic --- freqtrade/data/entryexitanalysis.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/freqtrade/data/entryexitanalysis.py b/freqtrade/data/entryexitanalysis.py index e217294a25b..da548973186 100644 --- a/freqtrade/data/entryexitanalysis.py +++ b/freqtrade/data/entryexitanalysis.py @@ -302,16 +302,16 @@ def print_results( def _merge_dfs(entry_df, exit_df, available_inds): merge_on = ["pair", "open_date"] columns_to_keep = merge_on + ["enter_reason", "exit_reason"] + available_inds - if exit_df is not None and not exit_df.empty: - merged_df = pd.merge( - entry_df[columns_to_keep], - exit_df[merge_on + available_inds], - on=merge_on, - suffixes=(" (entry)", " (exit)"), - ) - else: - merged_df = entry_df[columns_to_keep] - return merged_df + + if exit_df is None or exit_df.empty: + return entry_df[columns_to_keep] + + return pd.merge( + entry_df[columns_to_keep], + exit_df[merge_on + available_inds], + on=merge_on, + suffixes=(" (entry)", " (exit)"), + ) def _print_table( From 268683f8eafb17fb9c5525d5cdede9e151bdafd1 Mon Sep 17 00:00:00 2001 From: jainanuj94 Date: Mon, 19 Aug 2024 12:50:54 +0530 Subject: [PATCH 12/93] update documentation --- docs/advanced-backtesting.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/docs/advanced-backtesting.md b/docs/advanced-backtesting.md index 627ff20cd31..e1f7d77d41e 100644 --- a/docs/advanced-backtesting.md +++ b/docs/advanced-backtesting.md @@ -103,6 +103,10 @@ The indicators have to be present in your strategy's main DataFrame (either for timeframe or for informative timeframes) otherwise they will simply be ignored in the script output. +!!! note "Indicator List" + The indicator values will be displayed for both entry and exit points. If `--indicator-list all` is specified, + only the indicators at the entry point will be shown to avoid excessively large lists, which could occur depending on the strategy. + There are a range of candle and trade-related fields that are included in the analysis so are automatically accessible by including them on the indicator-list, and these include: @@ -141,14 +145,6 @@ Use the `--rejected-signals` option to print out rejected signals. freqtrade backtesting-analysis -c --rejected-signals ``` -### Printing analysis on exit signals - -Use the `--exit-signals` option to print out analysis on exited signals. - -```bash -freqtrade backtesting-analysis -c --exit-signals -``` - ### Writing tables to CSV Some of the tabular outputs can become large, so printing them out to the terminal is not preferable. From 08d5174d026f0cabce5262c216e0ddbf6a7445dd Mon Sep 17 00:00:00 2001 From: Anuj Jain Date: Wed, 4 Sep 2024 09:56:12 +0530 Subject: [PATCH 13/93] update documentation and add default values --- docs/advanced-backtesting.md | 21 +++++++++++++++++++++ freqtrade/data/entryexitanalysis.py | 6 ++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/docs/advanced-backtesting.md b/docs/advanced-backtesting.md index e1f7d77d41e..de1264ae8c9 100644 --- a/docs/advanced-backtesting.md +++ b/docs/advanced-backtesting.md @@ -122,6 +122,27 @@ automatically accessible by including them on the indicator-list, and these incl - **profit_ratio :** trade profit ratio - **profit_abs :** absolute profit return of the trade +#### Sample Output for Indicator Values + +```bash +freqtrade backtesting-analysis -c user_data/config.json --analysis-groups 0 --indicator-list chikou_span tenkan_sen +``` +In this example, +we aim to display the `chikou_span` and `tenkan_sen` indicator values at both the entry and exit points of trades. + +A sample output for indicators might look like this: + +| pair | open_date | enter_reason | exit_reason | chikou_span (entry) | tenkan_sen (entry) | chikou_span (exit) | tenkan_sen (exit) | +|-----------|---------------------------|--------------|-------------|---------------------|--------------------|--------------------|-------------------| +| DOGE/USDT | 2024-07-06 00:35:00+00:00 | | exit_signal | 0.105 | 0.106 | 0.105 | 0.107 | +| BTC/USDT | 2024-08-05 14:20:00+00:00 | | roi | 54643.440 | 51696.400 | 54386.000 | 52072.010 | + +As shown in the table, `chikou_span (entry)` represents the indicator value at the time of trade entry, +while `chikou_span (exit)` reflects its value at the time of exit. +This detailed view of indicator values enhances the analysis. + +The `(entry)` and `(exit)` suffixes are added to indicators +to distinguish the values at the entry and exit points of the trade. ### Filtering the trade output by date diff --git a/freqtrade/data/entryexitanalysis.py b/freqtrade/data/entryexitanalysis.py index da548973186..964c4f86f7b 100644 --- a/freqtrade/data/entryexitanalysis.py +++ b/freqtrade/data/entryexitanalysis.py @@ -51,7 +51,9 @@ def _load_exit_signal_candles(backtest_dir: Path) -> Dict[str, Dict[str, pd.Data return _load_backtest_analysis_data(backtest_dir, "exited") -def _process_candles_and_indicators(pairlist, strategy_name, trades, signal_candles, date_col: str): +def _process_candles_and_indicators( + pairlist, strategy_name, trades, signal_candles, date_col: str = "open_date" +): analysed_trades_dict: Dict[str, Dict] = {strategy_name: {}} try: @@ -69,7 +71,7 @@ def _process_candles_and_indicators(pairlist, strategy_name, trades, signal_cand def _analyze_candles_and_indicators( - pair: str, trades: pd.DataFrame, signal_candles: pd.DataFrame, date_col: str + pair: str, trades: pd.DataFrame, signal_candles: pd.DataFrame, date_col: str = "open_date" ) -> pd.DataFrame: buyf = signal_candles From 63092d7d1ae8dd9745740977d8a9e21858ea70dc Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Sep 2024 20:44:15 +0200 Subject: [PATCH 14/93] chore: re-add analytics to docs page --- mkdocs.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mkdocs.yml b/mkdocs.yml index 9e67f1f71e6..7633181f9ef 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -116,6 +116,9 @@ extra: version: provider: mike alias: true + analytics: + provider: google + property: G-VH170LG9M5 plugins: - search: enabled: true From 2fdf108198fe2efe9da483c2196ee69e0f0fd539 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Sep 2024 21:04:39 +0200 Subject: [PATCH 15/93] chore: update site_url to work correctly --- mkdocs.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 7633181f9ef..494b1875572 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,5 +1,5 @@ site_name: Freqtrade -site_url: !ENV [READTHEDOCS_CANONICAL_URL, 'https://www.freqtrade.io/en/latest/'] +site_url: !ENV [READTHEDOCS_CANONICAL_URL, 'https://www.freqtrade.io/'] site_description: Freqtrade is a free and open source crypto trading bot written in Python, designed to support all major exchanges and be controlled via Telegram or builtin Web UI repo_url: https://github.com/freqtrade/freqtrade edit_uri: edit/develop/docs/ @@ -116,9 +116,6 @@ extra: version: provider: mike alias: true - analytics: - provider: google - property: G-VH170LG9M5 plugins: - search: enabled: true From 824db78234477e6b3aaeecbd031e24da7fe0f551 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Sep 2024 21:07:16 +0200 Subject: [PATCH 16/93] chore: update site_url again --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 494b1875572..aa2d79cb205 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,5 +1,5 @@ site_name: Freqtrade -site_url: !ENV [READTHEDOCS_CANONICAL_URL, 'https://www.freqtrade.io/'] +site_url: !ENV [READTHEDOCS_CANONICAL_URL, 'https://www.freqtrade.io/en/'] site_description: Freqtrade is a free and open source crypto trading bot written in Python, designed to support all major exchanges and be controlled via Telegram or builtin Web UI repo_url: https://github.com/freqtrade/freqtrade edit_uri: edit/develop/docs/ From 65ba67dedcb7533e2cc8f476add3bdb47d3a5b39 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Sep 2024 21:08:34 +0200 Subject: [PATCH 17/93] chore: re-add analytics. --- mkdocs.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mkdocs.yml b/mkdocs.yml index aa2d79cb205..6d51e136b51 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -116,6 +116,9 @@ extra: version: provider: mike alias: true + analytics: + provider: google + property: G-VH170LG9M5 plugins: - search: enabled: true From c6a66a8fac7d16e77429015adfbbb9760bb5e613 Mon Sep 17 00:00:00 2001 From: xmatthias <5024695+xmatthias@users.noreply.github.com> Date: Thu, 5 Sep 2024 03:12:48 +0000 Subject: [PATCH 18/93] chore: update pre-commit hooks --- .../exchange/binance_leverage_tiers.json | 1180 +++++++++++------ 1 file changed, 776 insertions(+), 404 deletions(-) diff --git a/freqtrade/exchange/binance_leverage_tiers.json b/freqtrade/exchange/binance_leverage_tiers.json index 303b66eb8e0..859d87b9fd3 100644 --- a/freqtrade/exchange/binance_leverage_tiers.json +++ b/freqtrade/exchange/binance_leverage_tiers.json @@ -134,128 +134,144 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "75", + "notionalCap": "10000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 50000.0, + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 150000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { - "bracket": "2", + "bracket": "3", "initialLeverage": "25", - "notionalCap": "50000", - "notionalFloor": "5000", + "notionalCap": "150000", + "notionalFloor": "30000", "maintMarginRatio": "0.02", - "cum": "25.0" + "cum": "200.0" } }, { - "tier": 3.0, + "tier": 4.0, "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, + "minNotional": 150000.0, + "maxNotional": 300000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { - "bracket": "3", + "bracket": "4", "initialLeverage": "20", - "notionalCap": "100000", - "notionalFloor": "50000", + "notionalCap": "300000", + "notionalFloor": "150000", "maintMarginRatio": "0.025", - "cum": "275.0" + "cum": "950.0" } }, { - "tier": 4.0, + "tier": 5.0, "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 1000000.0, + "minNotional": 300000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "4", + "bracket": "5", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "100000", + "notionalCap": "1500000", + "notionalFloor": "300000", "maintMarginRatio": "0.05", - "cum": "2775.0" + "cum": "8450.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 1500000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "5", + "bracket": "6", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "3000000", + "notionalFloor": "1500000", "maintMarginRatio": "0.1", - "cum": "52775.0" + "cum": "83450.0" } }, { - "tier": 6.0, + "tier": 7.0, "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 2500000.0, + "minNotional": 3000000.0, + "maxNotional": 3750000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "6", + "bracket": "7", "initialLeverage": "4", - "notionalCap": "2500000", - "notionalFloor": "2000000", + "notionalCap": "3750000", + "notionalFloor": "3000000", "maintMarginRatio": "0.125", - "cum": "102775.0" + "cum": "158450.0" } }, { - "tier": 7.0, + "tier": 8.0, "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 3750000.0, + "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "7", + "bracket": "8", "initialLeverage": "2", - "notionalCap": "5000000", - "notionalFloor": "2500000", + "notionalCap": "7500000", + "notionalFloor": "3750000", "maintMarginRatio": "0.25", - "cum": "415275.0" + "cum": "627200.0" } }, { - "tier": 8.0, + "tier": 9.0, "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 7500000.0, + "maxNotional": 15000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "9", "initialLeverage": "1", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "15000000", + "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "1665275.0" + "cum": "2502200.0" } } ], @@ -1206,13 +1222,13 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.0065, - "maxLeverage": 50.0, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "75", + "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.0065", "cum": "0.0" @@ -1221,17 +1237,17 @@ { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, + "minNotional": 10000.0, "maxNotional": 25000.0, "maintenanceMarginRate": 0.0075, - "maxLeverage": 40.0, + "maxLeverage": 50.0, "info": { "bracket": "2", - "initialLeverage": "40", + "initialLeverage": "50", "notionalCap": "25000", - "notionalFloor": "5000", + "notionalFloor": "10000", "maintMarginRatio": "0.0075", - "cum": "5.0" + "cum": "10.0" } }, { @@ -1240,110 +1256,126 @@ "minNotional": 25000.0, "maxNotional": 150000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 25.0, + "maxLeverage": 40.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "40", "notionalCap": "150000", "notionalFloor": "25000", "maintMarginRatio": "0.01", - "cum": "67.5" + "cum": "72.5" } }, { "tier": 4.0, "currency": "USDT", "minNotional": 150000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "4", + "initialLeverage": "25", + "notionalCap": "300000", + "notionalFloor": "150000", + "maintMarginRatio": "0.02", + "cum": "1572.5" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 300000.0, "maxNotional": 600000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { - "bracket": "4", + "bracket": "5", "initialLeverage": "20", "notionalCap": "600000", - "notionalFloor": "150000", + "notionalFloor": "300000", "maintMarginRatio": "0.025", - "cum": "2317.5" + "cum": "3072.5" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", "minNotional": 600000.0, - "maxNotional": 1500000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "5", + "bracket": "6", "initialLeverage": "10", - "notionalCap": "1500000", + "notionalCap": "3000000", "notionalFloor": "600000", "maintMarginRatio": "0.05", - "cum": "17317.5" + "cum": "18072.5" } }, { - "tier": 6.0, + "tier": 7.0, "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 3000000.0, + "minNotional": 3000000.0, + "maxNotional": 6000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "6", + "bracket": "7", "initialLeverage": "5", - "notionalCap": "3000000", - "notionalFloor": "1500000", + "notionalCap": "6000000", + "notionalFloor": "3000000", "maintMarginRatio": "0.1", - "cum": "92317.5" + "cum": "168072.5" } }, { - "tier": 7.0, + "tier": 8.0, "currency": "USDT", - "minNotional": 3000000.0, - "maxNotional": 5000000.0, + "minNotional": 6000000.0, + "maxNotional": 7500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "7", + "bracket": "8", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "3000000", + "notionalCap": "7500000", + "notionalFloor": "6000000", "maintMarginRatio": "0.125", - "cum": "167317.5" + "cum": "318072.5" } }, { - "tier": 8.0, + "tier": 9.0, "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 9000000.0, + "minNotional": 7500000.0, + "maxNotional": 15000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "8", + "bracket": "9", "initialLeverage": "2", - "notionalCap": "9000000", - "notionalFloor": "5000000", + "notionalCap": "15000000", + "notionalFloor": "7500000", "maintMarginRatio": "0.25", - "cum": "792317.5" + "cum": "1255572.5" } }, { - "tier": 9.0, + "tier": 10.0, "currency": "USDT", - "minNotional": 9000000.0, + "minNotional": 15000000.0, "maxNotional": 30000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "9", + "bracket": "10", "initialLeverage": "1", "notionalCap": "30000000", - "notionalFloor": "9000000", + "notionalFloor": "15000000", "maintMarginRatio": "0.5", - "cum": "3042317.5" + "cum": "5005572.5" } } ], @@ -5270,13 +5302,13 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 10000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.0065, "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "10000", + "notionalCap": "25000", "notionalFloor": "5000", "maintMarginRatio": "0.0065", "cum": "7.5" @@ -5285,103 +5317,103 @@ { "tier": 3.0, "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.0075, + "minNotional": 25000.0, + "maxNotional": 80000.0, + "maintenanceMarginRate": 0.01, "maxLeverage": 40.0, "info": { "bracket": "3", "initialLeverage": "40", - "notionalCap": "25000", - "notionalFloor": "10000", - "maintMarginRatio": "0.0075", - "cum": "17.5" + "notionalCap": "80000", + "notionalFloor": "25000", + "maintMarginRatio": "0.01", + "cum": "95.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.01, + "minNotional": 80000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "4", "initialLeverage": "25", - "notionalCap": "50000", - "notionalFloor": "25000", - "maintMarginRatio": "0.01", - "cum": "80.0" + "notionalCap": "400000", + "notionalFloor": "80000", + "maintMarginRatio": "0.02", + "cum": "895.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 250000.0, + "minNotional": 400000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "5", "initialLeverage": "20", - "notionalCap": "250000", - "notionalFloor": "50000", + "notionalCap": "800000", + "notionalFloor": "400000", "maintMarginRatio": "0.025", - "cum": "830.0" + "cum": "2895.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, + "minNotional": 800000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "6", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "notionalCap": "4000000", + "notionalFloor": "800000", "maintMarginRatio": "0.05", - "cum": "7080.0" + "cum": "22895.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 4000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "7", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "8000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.1", - "cum": "57080.0" + "cum": "222895.0" } }, { "tier": 8.0, "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 5000000.0, + "minNotional": 8000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "8", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "notionalCap": "10000000", + "notionalFloor": "8000000", "maintMarginRatio": "0.125", - "cum": "107080.0" + "cum": "422895.0" } }, { "tier": 9.0, "currency": "USDT", - "minNotional": 5000000.0, + "minNotional": 10000000.0, "maxNotional": 20000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, @@ -5389,9 +5421,9 @@ "bracket": "9", "initialLeverage": "2", "notionalCap": "20000000", - "notionalFloor": "5000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.25", - "cum": "732080.0" + "cum": "1672895.0" } }, { @@ -5407,7 +5439,7 @@ "notionalCap": "50000000", "notionalFloor": "20000000", "maintMarginRatio": "0.5", - "cum": "5732080.0" + "cum": "6672895.0" } } ], @@ -6732,13 +6764,13 @@ "tier": 3.0, "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 50000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.01, "maxLeverage": 40.0, "info": { "bracket": "3", "initialLeverage": "40", - "notionalCap": "50000", + "notionalCap": "100000", "notionalFloor": "10000", "maintMarginRatio": "0.01", "cum": "42.5" @@ -6747,7 +6779,7 @@ { "tier": 4.0, "currency": "USDT", - "minNotional": 50000.0, + "minNotional": 100000.0, "maxNotional": 500000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, @@ -6755,95 +6787,95 @@ "bracket": "4", "initialLeverage": "25", "notionalCap": "500000", - "notionalFloor": "50000", + "notionalFloor": "100000", "maintMarginRatio": "0.02", - "cum": "542.5" + "cum": "1042.5" } }, { "tier": 5.0, "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 2000000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "5", - "initialLeverage": "10", - "notionalCap": "2000000", + "initialLeverage": "20", + "notionalCap": "1000000", "notionalFloor": "500000", - "maintMarginRatio": "0.05", - "cum": "15542.5" + "maintMarginRatio": "0.025", + "cum": "3542.5" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 4000000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "6", - "initialLeverage": "5", - "notionalCap": "4000000", - "notionalFloor": "2000000", - "maintMarginRatio": "0.1", - "cum": "115542.5" + "initialLeverage": "10", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.05", + "cum": "28542.5" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 4000000.0, - "maxNotional": 8000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "7", - "initialLeverage": "4", - "notionalCap": "8000000", - "notionalFloor": "4000000", - "maintMarginRatio": "0.125", - "cum": "215542.5" + "initialLeverage": "5", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.1", + "cum": "278542.5" } }, { "tier": 8.0, "currency": "USDT", - "minNotional": 8000000.0, + "minNotional": 10000000.0, "maxNotional": 15000000.0, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "8", - "initialLeverage": "3", + "initialLeverage": "4", "notionalCap": "15000000", - "notionalFloor": "8000000", - "maintMarginRatio": "0.15", - "cum": "415542.5" + "notionalFloor": "10000000", + "maintMarginRatio": "0.125", + "cum": "528542.5" } }, { "tier": 9.0, "currency": "USDT", "minNotional": 15000000.0, - "maxNotional": 20000000.0, + "maxNotional": 25000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "9", "initialLeverage": "2", - "notionalCap": "20000000", + "notionalCap": "25000000", "notionalFloor": "15000000", "maintMarginRatio": "0.25", - "cum": "1915542.5" + "cum": "2403542.5" } }, { "tier": 10.0, "currency": "USDT", - "minNotional": 20000000.0, + "minNotional": 25000000.0, "maxNotional": 50000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, @@ -6851,9 +6883,9 @@ "bracket": "10", "initialLeverage": "1", "notionalCap": "50000000", - "notionalFloor": "20000000", + "notionalFloor": "25000000", "maintMarginRatio": "0.5", - "cum": "6915542.5" + "cum": "8653542.5" } } ], @@ -10093,6 +10125,152 @@ } } ], + "CHESS/USDT:USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, + "info": { + "bracket": "1", + "initialLeverage": "75", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 40000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "40000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "175.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 40000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "40000", + "maintMarginRatio": "0.05", + "cum": "1175.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "11175.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "21175.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83675.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "333675.0" + } + } + ], "CHR/USDT:USDT": [ { "tier": 1.0, @@ -12508,13 +12686,13 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, "maxLeverage": 75.0, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "5000", + "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" @@ -12523,129 +12701,129 @@ { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, + "minNotional": 10000.0, + "maxNotional": 40000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "10000", - "notionalFloor": "5000", + "notionalCap": "40000", + "notionalFloor": "10000", "maintMarginRatio": "0.015", - "cum": "25.0" + "cum": "50.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 20000.0, + "minNotional": 40000.0, + "maxNotional": 200000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "20000", - "notionalFloor": "10000", + "notionalCap": "200000", + "notionalFloor": "40000", "maintMarginRatio": "0.02", - "cum": "75.0" + "cum": "250.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 40000.0, + "minNotional": 200000.0, + "maxNotional": 400000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "4", "initialLeverage": "20", - "notionalCap": "40000", - "notionalFloor": "20000", + "notionalCap": "400000", + "notionalFloor": "200000", "maintMarginRatio": "0.025", - "cum": "175.0" + "cum": "1250.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 40000.0, - "maxNotional": 200000.0, + "minNotional": 400000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "200000", - "notionalFloor": "40000", + "notionalCap": "2000000", + "notionalFloor": "400000", "maintMarginRatio": "0.05", - "cum": "1175.0" + "cum": "11250.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 400000.0, + "minNotional": 2000000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "400000", - "notionalFloor": "200000", + "notionalCap": "4000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.1", - "cum": "11175.0" + "cum": "111250.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 400000.0, - "maxNotional": 500000.0, + "minNotional": 4000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "500000", - "notionalFloor": "400000", + "notionalCap": "5000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.125", - "cum": "21175.0" + "cum": "211250.0" } }, { "tier": 8.0, "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "500000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.25", - "cum": "83675.0" + "cum": "836250.0" } }, { "tier": 9.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.5", - "cum": "333675.0" + "cum": "3336250.0" } } ], @@ -16027,6 +16205,152 @@ } } ], + "FLUX/USDT:USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, + "info": { + "bracket": "1", + "initialLeverage": "75", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 40000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "40000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "175.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 40000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "40000", + "maintMarginRatio": "0.05", + "cum": "1175.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "11175.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "21175.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83675.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "333675.0" + } + } + ], "FRONT/USDT:USDT": [ { "tier": 1.0, @@ -24150,128 +24474,144 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "75", + "notionalCap": "10000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 25000.0, + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { - "bracket": "2", + "bracket": "3", "initialLeverage": "25", - "notionalCap": "25000", - "notionalFloor": "5000", + "notionalCap": "100000", + "notionalFloor": "20000", "maintMarginRatio": "0.02", - "cum": "25.0" + "cum": "150.0" } }, { - "tier": 3.0, + "tier": 4.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 50000.0, + "minNotional": 100000.0, + "maxNotional": 200000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { - "bracket": "3", + "bracket": "4", "initialLeverage": "20", - "notionalCap": "50000", - "notionalFloor": "25000", + "notionalCap": "200000", + "notionalFloor": "100000", "maintMarginRatio": "0.025", - "cum": "150.0" + "cum": "650.0" } }, { - "tier": 4.0, + "tier": 5.0, "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 500000.0, + "minNotional": 200000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "4", + "bracket": "5", "initialLeverage": "10", - "notionalCap": "500000", - "notionalFloor": "50000", + "notionalCap": "1000000", + "notionalFloor": "200000", "maintMarginRatio": "0.05", - "cum": "1400.0" + "cum": "5650.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, + "minNotional": 1000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "5", + "bracket": "6", "initialLeverage": "5", - "notionalCap": "1000000", - "notionalFloor": "500000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.1", - "cum": "26400.0" + "cum": "55650.0" } }, { - "tier": 6.0, + "tier": 7.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 1250000.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "6", + "bracket": "7", "initialLeverage": "4", - "notionalCap": "1250000", - "notionalFloor": "1000000", + "notionalCap": "2500000", + "notionalFloor": "2000000", "maintMarginRatio": "0.125", - "cum": "51400.0" + "cum": "105650.0" } }, { - "tier": 7.0, + "tier": 8.0, "currency": "USDT", - "minNotional": 1250000.0, - "maxNotional": 2500000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "7", + "bracket": "8", "initialLeverage": "2", - "notionalCap": "2500000", - "notionalFloor": "1250000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.25", - "cum": "207650.0" + "cum": "418150.0" } }, { - "tier": 8.0, + "tier": 9.0, "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "9", "initialLeverage": "1", - "notionalCap": "5000000", - "notionalFloor": "2500000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.5", - "cum": "832650.0" + "cum": "1668150.0" } } ], @@ -33748,13 +34088,13 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50.0, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "75", + "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" @@ -33763,113 +34103,129 @@ { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 50000.0, + "minNotional": 10000.0, + "maxNotional": 80000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "80000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 80000.0, + "maxNotional": 400000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { - "bracket": "2", + "bracket": "3", "initialLeverage": "25", - "notionalCap": "50000", - "notionalFloor": "5000", + "notionalCap": "400000", + "notionalFloor": "80000", "maintMarginRatio": "0.02", - "cum": "50.0" + "cum": "450.0" } }, { - "tier": 3.0, + "tier": 4.0, "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 600000.0, + "minNotional": 400000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { - "bracket": "3", + "bracket": "4", "initialLeverage": "20", - "notionalCap": "600000", - "notionalFloor": "50000", + "notionalCap": "800000", + "notionalFloor": "400000", "maintMarginRatio": "0.025", - "cum": "300.0" + "cum": "2450.0" } }, { - "tier": 4.0, + "tier": 5.0, "currency": "USDT", - "minNotional": 600000.0, - "maxNotional": 1200000.0, + "minNotional": 800000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "4", + "bracket": "5", "initialLeverage": "10", - "notionalCap": "1200000", - "notionalFloor": "600000", + "notionalCap": "4000000", + "notionalFloor": "800000", "maintMarginRatio": "0.05", - "cum": "15300.0" + "cum": "22450.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 1200000.0, - "maxNotional": 3000000.0, + "minNotional": 4000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "5", + "bracket": "6", "initialLeverage": "5", - "notionalCap": "3000000", - "notionalFloor": "1200000", + "notionalCap": "8000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.1", - "cum": "75300.0" + "cum": "222450.0" } }, { - "tier": 6.0, + "tier": 7.0, "currency": "USDT", - "minNotional": 3000000.0, - "maxNotional": 4000000.0, + "minNotional": 8000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "6", + "bracket": "7", "initialLeverage": "4", - "notionalCap": "4000000", - "notionalFloor": "3000000", + "notionalCap": "10000000", + "notionalFloor": "8000000", "maintMarginRatio": "0.125", - "cum": "150300.0" + "cum": "422450.0" } }, { - "tier": 7.0, + "tier": 8.0, "currency": "USDT", - "minNotional": 4000000.0, - "maxNotional": 6000000.0, + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "7", + "bracket": "8", "initialLeverage": "2", - "notionalCap": "6000000", - "notionalFloor": "4000000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.25", - "cum": "650300.0" + "cum": "1672450.0" } }, { - "tier": 8.0, + "tier": 9.0, "currency": "USDT", - "minNotional": 6000000.0, - "maxNotional": 10000000.0, + "minNotional": 20000000.0, + "maxNotional": 40000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "9", "initialLeverage": "1", - "notionalCap": "10000000", - "notionalFloor": "6000000", + "notionalCap": "40000000", + "notionalFloor": "20000000", "maintMarginRatio": "0.5", - "cum": "2150300.0" + "cum": "6672450.0" } } ], @@ -35634,128 +35990,144 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "75", + "notionalCap": "10000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 50000.0, + "minNotional": 10000.0, + "maxNotional": 80000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "80000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 80000.0, + "maxNotional": 400000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { - "bracket": "2", + "bracket": "3", "initialLeverage": "25", - "notionalCap": "50000", - "notionalFloor": "5000", + "notionalCap": "400000", + "notionalFloor": "80000", "maintMarginRatio": "0.02", - "cum": "25.0" + "cum": "450.0" } }, { - "tier": 3.0, + "tier": 4.0, "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, + "minNotional": 400000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { - "bracket": "3", + "bracket": "4", "initialLeverage": "20", - "notionalCap": "100000", - "notionalFloor": "50000", + "notionalCap": "800000", + "notionalFloor": "400000", "maintMarginRatio": "0.025", - "cum": "275.0" + "cum": "2450.0" } }, { - "tier": 4.0, + "tier": 5.0, "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 1000000.0, + "minNotional": 800000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "4", + "bracket": "5", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "100000", + "notionalCap": "4000000", + "notionalFloor": "800000", "maintMarginRatio": "0.05", - "cum": "2775.0" + "cum": "22450.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 4000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "5", + "bracket": "6", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "8000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.1", - "cum": "52775.0" + "cum": "222450.0" } }, { - "tier": 6.0, + "tier": 7.0, "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 2500000.0, + "minNotional": 8000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "6", + "bracket": "7", "initialLeverage": "4", - "notionalCap": "2500000", - "notionalFloor": "2000000", + "notionalCap": "10000000", + "notionalFloor": "8000000", "maintMarginRatio": "0.125", - "cum": "102775.0" + "cum": "422450.0" } }, { - "tier": 7.0, + "tier": 8.0, "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "7", + "bracket": "8", "initialLeverage": "2", - "notionalCap": "5000000", - "notionalFloor": "2500000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.25", - "cum": "415275.0" + "cum": "1672450.0" } }, { - "tier": 8.0, + "tier": 9.0, "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 20000000.0, + "maxNotional": 40000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "9", "initialLeverage": "1", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "40000000", + "notionalFloor": "20000000", "maintMarginRatio": "0.5", - "cum": "1665275.0" + "cum": "6672450.0" } } ], @@ -38316,13 +38688,13 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 25000.0, + "maxNotional": 60000.0, "maintenanceMarginRate": 0.01, "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "25000", + "notionalCap": "60000", "notionalFloor": "5000", "maintMarginRatio": "0.01", "cum": "20.0" @@ -38331,39 +38703,39 @@ { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 150000.0, + "minNotional": 60000.0, + "maxNotional": 300000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "150000", - "notionalFloor": "25000", + "notionalCap": "300000", + "notionalFloor": "60000", "maintMarginRatio": "0.02", - "cum": "270.0" + "cum": "620.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 150000.0, - "maxNotional": 300000.0, + "minNotional": 300000.0, + "maxNotional": 600000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "4", "initialLeverage": "20", - "notionalCap": "300000", - "notionalFloor": "150000", + "notionalCap": "600000", + "notionalFloor": "300000", "maintMarginRatio": "0.025", - "cum": "1020.0" + "cum": "2120.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 300000.0, + "minNotional": 600000.0, "maxNotional": 3000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, @@ -38371,9 +38743,9 @@ "bracket": "5", "initialLeverage": "10", "notionalCap": "3000000", - "notionalFloor": "300000", + "notionalFloor": "600000", "maintMarginRatio": "0.05", - "cum": "8520.0" + "cum": "17120.0" } }, { @@ -38389,7 +38761,7 @@ "notionalCap": "6000000", "notionalFloor": "3000000", "maintMarginRatio": "0.1", - "cum": "158520.0" + "cum": "167120.0" } }, { @@ -38405,7 +38777,7 @@ "notionalCap": "7500000", "notionalFloor": "6000000", "maintMarginRatio": "0.125", - "cum": "308520.0" + "cum": "317120.0" } }, { @@ -38421,7 +38793,7 @@ "notionalCap": "15000000", "notionalFloor": "7500000", "maintMarginRatio": "0.25", - "cum": "1246020.0" + "cum": "1254620.0" } }, { @@ -38437,7 +38809,7 @@ "notionalCap": "30000000", "notionalFloor": "15000000", "maintMarginRatio": "0.5", - "cum": "4996020.0" + "cum": "5004620.0" } } ], From b7145debfb04b03916e3b3a39cc743b4959cd12b Mon Sep 17 00:00:00 2001 From: Anuj Jain Date: Thu, 5 Sep 2024 21:52:09 +0530 Subject: [PATCH 19/93] handle trade wide indicators --- docs/advanced-backtesting.md | 4 ++++ freqtrade/data/entryexitanalysis.py | 11 ++++++++++- tests/data/test_entryexitanalysis.py | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/advanced-backtesting.md b/docs/advanced-backtesting.md index de1264ae8c9..5004dfc2adc 100644 --- a/docs/advanced-backtesting.md +++ b/docs/advanced-backtesting.md @@ -144,6 +144,10 @@ This detailed view of indicator values enhances the analysis. The `(entry)` and `(exit)` suffixes are added to indicators to distinguish the values at the entry and exit points of the trade. +!!! note "Trade-wide Indicators" + Certain trade-wide indicators do not have the `(entry)` or `(exit)` suffix. These indicators include: + `"open_date"`, `"close_date"`, `"min_rate"`, `"max_rate"`, `"profit_ratio"`, and `"profit_abs"`. + ### Filtering the trade output by date To show only trades between dates within your backtested timerange, supply the usual `timerange` option in `YYYYMMDD-[YYYYMMDD]` format: diff --git a/freqtrade/data/entryexitanalysis.py b/freqtrade/data/entryexitanalysis.py index 964c4f86f7b..974e39d399e 100644 --- a/freqtrade/data/entryexitanalysis.py +++ b/freqtrade/data/entryexitanalysis.py @@ -303,6 +303,15 @@ def print_results( def _merge_dfs(entry_df, exit_df, available_inds): merge_on = ["pair", "open_date"] + trade_wide_indicators = [ + "open_date", + "close_date", + "min_rate", + "max_rate", + "profit_ratio", + "profit_abs", + ] + signal_wide_indicators = list(set(available_inds) - set(trade_wide_indicators)) columns_to_keep = merge_on + ["enter_reason", "exit_reason"] + available_inds if exit_df is None or exit_df.empty: @@ -310,7 +319,7 @@ def _merge_dfs(entry_df, exit_df, available_inds): return pd.merge( entry_df[columns_to_keep], - exit_df[merge_on + available_inds], + exit_df[merge_on + signal_wide_indicators], on=merge_on, suffixes=(" (entry)", " (exit)"), ) diff --git a/tests/data/test_entryexitanalysis.py b/tests/data/test_entryexitanalysis.py index 39456b7a389..374b84fc745 100644 --- a/tests/data/test_entryexitanalysis.py +++ b/tests/data/test_entryexitanalysis.py @@ -168,6 +168,7 @@ def test_backtest_analysis_on_entry_and_rejected_signals_nomock( assert "close (exit)" in captured.out assert "rsi (exit)" in captured.out assert "52.829" in captured.out + assert "profit_abs" in captured.out # test group 1 args = get_args(base_args + ["--analysis-groups", "1"]) From 8d96844312cdd5f398107ee102313796a44ce5df Mon Sep 17 00:00:00 2001 From: Anuj Jain Date: Fri, 6 Sep 2024 12:28:02 +0530 Subject: [PATCH 20/93] use BT_DATA_COLUMNS for trade wide indicators --- docs/advanced-backtesting.md | 6 ++++-- freqtrade/data/entryexitanalysis.py | 11 ++--------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/docs/advanced-backtesting.md b/docs/advanced-backtesting.md index 5004dfc2adc..a6b30b5f86d 100644 --- a/docs/advanced-backtesting.md +++ b/docs/advanced-backtesting.md @@ -145,8 +145,10 @@ The `(entry)` and `(exit)` suffixes are added to indicators to distinguish the values at the entry and exit points of the trade. !!! note "Trade-wide Indicators" - Certain trade-wide indicators do not have the `(entry)` or `(exit)` suffix. These indicators include: - `"open_date"`, `"close_date"`, `"min_rate"`, `"max_rate"`, `"profit_ratio"`, and `"profit_abs"`. + Certain trade-wide indicators do not have the `(entry)` or `(exit)` suffix. These indicators include: `pair`, `stake_amount`, + `max_stake_amount`, `amount`, `open_date`, `close_date`, `open_rate`, `close_rate`, `fee_open`, `fee_close`, `trade_duration`, + `profit_ratio`, `profit_abs`, `exit_reason`,`initial_stop_loss_abs`, `initial_stop_loss_ratio`, `stop_loss_abs`, `stop_loss_ratio`, + `min_rate`, `max_rate`, `is_open`, `enter_tag`, `leverage`, `is_short`, `open_timestamp`, `close_timestamp` and `orders` ### Filtering the trade output by date diff --git a/freqtrade/data/entryexitanalysis.py b/freqtrade/data/entryexitanalysis.py index 974e39d399e..8077e104a06 100644 --- a/freqtrade/data/entryexitanalysis.py +++ b/freqtrade/data/entryexitanalysis.py @@ -8,6 +8,7 @@ from freqtrade.configuration import TimeRange from freqtrade.constants import Config from freqtrade.data.btanalysis import ( + BT_DATA_COLUMNS, get_latest_backtest_filename, load_backtest_data, load_backtest_stats, @@ -303,15 +304,7 @@ def print_results( def _merge_dfs(entry_df, exit_df, available_inds): merge_on = ["pair", "open_date"] - trade_wide_indicators = [ - "open_date", - "close_date", - "min_rate", - "max_rate", - "profit_ratio", - "profit_abs", - ] - signal_wide_indicators = list(set(available_inds) - set(trade_wide_indicators)) + signal_wide_indicators = list(set(available_inds) - set(BT_DATA_COLUMNS)) columns_to_keep = merge_on + ["enter_reason", "exit_reason"] + available_inds if exit_df is None or exit_df.empty: From 6a4b6412509f4c758fa615504e73abfed305e9dd Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 6 Sep 2024 20:58:54 +0200 Subject: [PATCH 21/93] feat: implement __str__ for marign and tradingmode enums --- freqtrade/enums/marginmode.py | 3 +++ freqtrade/enums/tradingmode.py | 3 +++ freqtrade/exchange/binance.py | 2 +- freqtrade/exchange/exchange.py | 4 ++-- freqtrade/persistence/trade_model.py | 4 ++-- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/freqtrade/enums/marginmode.py b/freqtrade/enums/marginmode.py index 0e8887a9aa7..9aa814c3981 100644 --- a/freqtrade/enums/marginmode.py +++ b/freqtrade/enums/marginmode.py @@ -11,3 +11,6 @@ class MarginMode(str, Enum): CROSS = "cross" ISOLATED = "isolated" NONE = "" + + def __str__(self): + return f"{self.name.lower()}" diff --git a/freqtrade/enums/tradingmode.py b/freqtrade/enums/tradingmode.py index 62f9b4255d4..a681d60f98e 100644 --- a/freqtrade/enums/tradingmode.py +++ b/freqtrade/enums/tradingmode.py @@ -10,3 +10,6 @@ class TradingMode(str, Enum): SPOT = "spot" MARGIN = "margin" FUTURES = "futures" + + def __str__(self): + return f"{self.name.lower()}" diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index d2e74f9eb00..d7fb0a353a9 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -192,7 +192,7 @@ def dry_run_liquidation_price( if maintenance_amt is None: raise OperationalException( "Parameter maintenance_amt is required by Binance.liquidation_price" - f"for {self.trading_mode.value}" + f"for {self.trading_mode}" ) if self.trading_mode == TradingMode.FUTURES: diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index cb1c1a49a0f..684eaa5d8a8 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -707,7 +707,7 @@ def validate_pairs(self, pairs: List[str]) -> None: # Note: ccxt has BaseCurrency/QuoteCurrency format for pairs if self.markets and pair not in self.markets: raise OperationalException( - f"Pair {pair} is not available on {self.name} {self.trading_mode.value}. " + f"Pair {pair} is not available on {self.name} {self.trading_mode}. " f"Please remove {pair} from your whitelist." ) @@ -890,7 +890,7 @@ def validate_trading_mode_and_margin_mode( ): mm_value = margin_mode and margin_mode.value raise OperationalException( - f"Freqtrade does not support {mm_value} {trading_mode.value} on {self.name}" + f"Freqtrade does not support {mm_value} {trading_mode} on {self.name}" ) def get_option(self, param: str, default: Optional[Any] = None) -> Any: diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index 4e7f01906ea..49afd927b9b 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -623,7 +623,7 @@ def __init__(self, **kwargs): self.orders = [] if self.trading_mode == TradingMode.MARGIN and self.interest_rate is None: raise OperationalException( - f"{self.trading_mode.value} trading requires param interest_rate on trades" + f"{self.trading_mode} trading requires param interest_rate on trades" ) def __repr__(self): @@ -1079,7 +1079,7 @@ def calc_close_trade_value(self, rate: float, amount: Optional[float] = None) -> return float(self._calc_base_close(amount1, rate, self.fee_close)) + funding_fees else: raise OperationalException( - f"{self.trading_mode.value} trading is not yet available using freqtrade" + f"{self.trading_mode} trading is not yet available using freqtrade" ) def calc_profit( From f714e306da948707034b189a2af3b296cd10d0a9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 6 Sep 2024 21:15:10 +0200 Subject: [PATCH 22/93] test: add margin and trading mode to test config --- tests/conftest.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index 8f15388ef37..99c42de5f48 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -617,6 +617,8 @@ def get_default_conf(testdatadir): "dataformat_ohlcv": "feather", "dataformat_trades": "feather", "runmode": "dry_run", + "trading_mode": "spot", + "margin_mode": "", "candle_type_def": CandleType.SPOT, } return configuration From 1a2578a4b76891ce18bc04dd558b36ae572524ff Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 7 Sep 2024 08:47:45 +0200 Subject: [PATCH 23/93] feat: Add margin/Trading mode output to bt-output --- .../optimize/optimize_reports/bt_output.py | 20 +++++++++++++++++++ .../optimize_reports/optimize_reports.py | 2 ++ 2 files changed, 22 insertions(+) diff --git a/freqtrade/optimize/optimize_reports/bt_output.py b/freqtrade/optimize/optimize_reports/bt_output.py index ef58975ca39..d509b75fbd5 100644 --- a/freqtrade/optimize/optimize_reports/bt_output.py +++ b/freqtrade/optimize/optimize_reports/bt_output.py @@ -263,12 +263,32 @@ def text_table_add_metrics(strat_results: Dict) -> None: else [] ) + trading_mode = ( + ( + [ + ( + "Trading Mode", + ( + "" + if not strat_results.get("margin_mode") + or strat_results.get("trading_mode", "spot") == "spot" + else f"{strat_results['margin_mode'].capitalize()} " + ) + + f"{strat_results['trading_mode'].capitalize()}", + ) + ] + ) + if "trading_mode" in strat_results + else [] + ) + # Newly added fields should be ignored if they are missing in strat_results. hyperopt-show # command stores these results and newer version of freqtrade must be able to handle old # results with missing new fields. metrics = [ ("Backtesting from", strat_results["backtest_start"]), ("Backtesting to", strat_results["backtest_end"]), + *trading_mode, ("Max open trades", strat_results["max_open_trades"]), ("", ""), # Empty line to improve readability ( diff --git a/freqtrade/optimize/optimize_reports/optimize_reports.py b/freqtrade/optimize/optimize_reports/optimize_reports.py index 38a6cfbf838..c3bb607dde5 100644 --- a/freqtrade/optimize/optimize_reports/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports/optimize_reports.py @@ -504,6 +504,8 @@ def generate_strategy_stats( "exit_profit_only": config["exit_profit_only"], "exit_profit_offset": config["exit_profit_offset"], "ignore_roi_if_entry_signal": config["ignore_roi_if_entry_signal"], + "trading_mode": config["trading_mode"], + "margin_mode": config["margin_mode"], **periodic_breakdown, **daily_stats, **trade_stats, From d9ec66695c8c9d4256086839c530eae14f23720c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 7 Sep 2024 08:49:30 +0200 Subject: [PATCH 24/93] docs: update backtesting docs with new row --- docs/backtesting.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/backtesting.md b/docs/backtesting.md index 2feba7ada8b..12a6c346ed1 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -293,6 +293,7 @@ A backtesting result will look like that: |-----------------------------+---------------------| | Backtesting from | 2019-01-01 00:00:00 | | Backtesting to | 2019-05-01 00:00:00 | +| Trading Mode | Spot | | Max open trades | 3 | | | | | Total/Daily Avg Trades | 429 / 3.575 | @@ -398,6 +399,7 @@ It contains some useful key metrics about performance of your strategy on backte |-----------------------------+---------------------| | Backtesting from | 2019-01-01 00:00:00 | | Backtesting to | 2019-05-01 00:00:00 | +| Trading Mode | Spot | | Max open trades | 3 | | | | | Total/Daily Avg Trades | 429 / 3.575 | @@ -452,6 +454,7 @@ It contains some useful key metrics about performance of your strategy on backte - `Backtesting from` / `Backtesting to`: Backtesting range (usually defined with the `--timerange` option). - `Max open trades`: Setting of `max_open_trades` (or `--max-open-trades`) - or number of pairs in the pairlist (whatever is lower). +- `Trading Mode`: Spot or Futures trading. - `Total/Daily Avg Trades`: Identical to the total trades of the backtest output table / Total trades divided by the backtesting duration in days (this will give you information about how many trades to expect from the strategy). - `Starting balance`: Start balance - as given by dry-run-wallet (config or command line). - `Final balance`: Final balance - starting balance + absolute profit. From 1b00f512c1e546620697c79bd99ebcddd80b19dc Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 7 Sep 2024 09:24:21 +0200 Subject: [PATCH 25/93] fix: call order_filled callback for left open trades --- freqtrade/optimize/backtesting.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 7ea0b44c980..828908a4ca1 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -1165,12 +1165,10 @@ def handle_left_open( self._exit_trade( trade, exit_row, exit_row[OPEN_IDX], trade.amount, ExitType.FORCE_EXIT.value ) - trade.orders[-1].close_bt_order(exit_row[DATE_IDX].to_pydatetime(), trade) - - trade.close_date = exit_row[DATE_IDX].to_pydatetime() trade.exit_reason = ExitType.FORCE_EXIT.value - trade.close(exit_row[OPEN_IDX], show_msg=False) - LocalTrade.close_bt_trade(trade) + self._process_exit_order( + trade.orders[-1], trade, exit_row[DATE_IDX].to_pydatetime(), exit_row, pair + ) def trade_slot_available(self, open_trade_count: int) -> bool: # Always allow trades when max_open_trades is enabled. From f95cc960e1db63584a8d226cbb2778d02dcbd5e3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 7 Sep 2024 09:25:20 +0200 Subject: [PATCH 26/93] test: tests should consider additional ff-update call --- tests/optimize/test_backtesting.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index d159c86022f..b2523079167 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -941,7 +941,7 @@ def custom_entry_price(proposed_rate, **kwargs): "use_detail,exp_funding_fee, exp_ff_updates", [ (True, -0.018054162, 11), - (False, -0.01780296, 5), + (False, -0.01780296, 6), ], ) def test_backtest_one_detail_futures( @@ -1051,8 +1051,8 @@ def custom_entry_price(proposed_rate, **kwargs): @pytest.mark.parametrize( "use_detail,entries,max_stake,ff_updates,expected_ff", [ - (True, 50, 3000, 54, -1.18038144), - (False, 6, 360, 10, -0.14679994), + (True, 50, 3000, 55, -1.18038144), + (False, 6, 360, 11, -0.14679994), ], ) def test_backtest_one_detail_futures_funding_fees( From 704e32b0dc18034739337078e324882eded02e50 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 7 Sep 2024 09:28:35 +0200 Subject: [PATCH 27/93] feat: properly parse marginmode on startup --- freqtrade/configuration/configuration.py | 10 +++++++++- freqtrade/freqtradebot.py | 8 +++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index d9c860abdff..1bbc8486170 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -15,7 +15,14 @@ from freqtrade.configuration.environment_vars import enironment_vars_to_dict from freqtrade.configuration.load_config import load_file, load_from_files from freqtrade.constants import Config -from freqtrade.enums import NON_UTIL_MODES, TRADE_MODES, CandleType, RunMode, TradingMode +from freqtrade.enums import ( + NON_UTIL_MODES, + TRADE_MODES, + CandleType, + MarginMode, + RunMode, + TradingMode, +) from freqtrade.exceptions import OperationalException from freqtrade.loggers import setup_logging from freqtrade.misc import deep_merge_dicts, parse_db_uri_for_logging @@ -389,6 +396,7 @@ def _process_data_options(self, config: Config) -> None: config.get("trading_mode", "spot") or "spot" ) config["trading_mode"] = TradingMode(config.get("trading_mode", "spot") or "spot") + config["margin_mode"] = MarginMode(config.get("margin_mode", "") or "") self._args_to_config( config, argname="candle_types", logstring="Detected --candle-types: {}" ) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index dff99e93e77..15a3ae6ee53 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -22,6 +22,7 @@ from freqtrade.enums import ( ExitCheckTuple, ExitType, + MarginMode, RPCMessageType, SignalDirection, State, @@ -108,6 +109,7 @@ def __init__(self, config: Config) -> None: PairLocks.timeframe = self.config["timeframe"] self.trading_mode: TradingMode = self.config.get("trading_mode", TradingMode.SPOT) + self.margin_mode: MarginMode = self.config.get("margin_mode", MarginMode.NONE) self.last_process: Optional[datetime] = None # RPC runs in separate threads, can start handling external commands just after @@ -2216,7 +2218,11 @@ def _update_trade_after_fill(self, trade: Trade, order: Order, send_msg: bool) - # TODO: should shorting/leverage be supported by Edge, # then this will need to be fixed. trade.adjust_stop_loss(trade.open_rate, self.strategy.stoploss, initial=True) - if order.ft_order_side == trade.entry_side or (trade.amount > 0 and trade.is_open): + if ( + order.ft_order_side == trade.entry_side + or (trade.amount > 0 and trade.is_open) + or self.margin_mode == MarginMode.CROSS + ): # Must also run for partial exits # TODO: Margin will need to use interest_rate as well. # interest_rate = self.exchange.get_interest_rate() From 0858e0a21ecfc6be391bf9f79741a32a3f4a5bbd Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 7 Sep 2024 15:29:23 +0200 Subject: [PATCH 28/93] Minor update to docs --- docs/advanced-backtesting.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/advanced-backtesting.md b/docs/advanced-backtesting.md index a6b30b5f86d..b97db79c527 100644 --- a/docs/advanced-backtesting.md +++ b/docs/advanced-backtesting.md @@ -19,14 +19,12 @@ freqtrade backtesting -c --timeframe --strategy Date: Sat, 7 Sep 2024 18:28:56 +0200 Subject: [PATCH 29/93] feat(bybit): add support for unified Accounts --- docs/exchanges.md | 12 +++++++++--- freqtrade/exchange/bybit.py | 6 ++---- tests/exchange/test_bybit.py | 12 +++++------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/exchanges.md b/docs/exchanges.md index f3550e97e76..f55c45919fa 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -255,18 +255,24 @@ The configuration parameter `exchange.unknown_fee_rate` can be used to specify t ## Bybit Futures trading on bybit is currently supported for USDT markets, and will use isolated futures mode. -Users with unified accounts (there's no way back) can create a Sub-account which will start as "non-unified", and can therefore use isolated futures. -On startup, freqtrade will set the position mode to "One-way Mode" for the whole (sub)account. This avoids making this call over and over again (slowing down bot operations), but means that changes to this setting may result in exceptions and errors + +On startup, freqtrade will set the position mode to "One-way Mode" for the whole (sub)account. This avoids making this call over and over again (slowing down bot operations), but means that changes to this setting may result in exceptions and errors. As bybit doesn't provide funding rate history, the dry-run calculation is used for live trades as well. -API Keys for live futures trading (Subaccount on non-unified) must have the following permissions: +API Keys for live futures trading must have the following permissions: * Read-write * Contract - Orders * Contract - Positions We do strongly recommend to limit all API keys to the IP you're going to use it from. +!!! Warning "Unified accounts" + Freqtrade assumes accounts to be dedicated to the bot. + We therefore recommend the usage of one subaccount per bot. This is especially important when using unified accounts. + Other configurations (multiple bots on one account, manual non-bot trades on the bot account) are not supported and may lead to unexpected behavior. + + !!! Tip "Stoploss on Exchange" Bybit (futures only) supports `stoploss_on_exchange` and uses `stop-loss-limit` orders. It provides great advantages, so we recommend to benefit from it by enabling stoploss on exchange. On futures, Bybit supports both `stop-limit` as well as `stop-market` orders. You can use either `"limit"` or `"market"` in the `order_types.stoploss` configuration setting to decide which type to use. diff --git a/freqtrade/exchange/bybit.py b/freqtrade/exchange/bybit.py index 52cf37d31af..af00710396e 100644 --- a/freqtrade/exchange/bybit.py +++ b/freqtrade/exchange/bybit.py @@ -90,10 +90,8 @@ def additional_exchange_init(self) -> None: # Returns a tuple of bools, first for margin, second for Account if is_unified and len(is_unified) > 1 and is_unified[1]: self.unified_account = True - logger.info("Bybit: Unified account.") - raise OperationalException( - "Bybit: Unified account is not supported. " - "Please use a standard (sub)account." + logger.info( + "Bybit: Unified account. Assuming dedicated subaccount for this bot." ) else: self.unified_account = False diff --git a/tests/exchange/test_bybit.py b/tests/exchange/test_bybit.py index 8dc11667c04..c72d5ae0dea 100644 --- a/tests/exchange/test_bybit.py +++ b/tests/exchange/test_bybit.py @@ -27,13 +27,11 @@ def test_additional_exchange_init_bybit(default_conf, mocker, caplog): api_mock.set_position_mode.reset_mock() api_mock.is_unified_enabled = MagicMock(return_value=[False, True]) - with pytest.raises(OperationalException, match=r"Bybit: Unified account is not supported.*"): - get_patched_exchange(mocker, default_conf, exchange="bybit", api_mock=api_mock) - assert log_has("Bybit: Unified account.", caplog) - # exchange = get_patched_exchange(mocker, default_conf, exchange="bybit", api_mock=api_mock) - # assert api_mock.set_position_mode.call_count == 1 - # assert api_mock.is_unified_enabled.call_count == 1 - # assert exchange.unified_account is True + exchange = get_patched_exchange(mocker, default_conf, exchange="bybit", api_mock=api_mock) + assert log_has("Bybit: Unified account. Assuming dedicated subaccount for this bot.", caplog) + assert api_mock.set_position_mode.call_count == 1 + assert api_mock.is_unified_enabled.call_count == 1 + assert exchange.unified_account is True ccxt_exceptionhandlers( mocker, default_conf, api_mock, "bybit", "additional_exchange_init", "set_position_mode" From a1681cdd637a42e66e5c5248cbb800f3dab10373 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Sep 2024 08:24:48 +0200 Subject: [PATCH 30/93] chore: improve typing --- freqtrade/data/converter/orderflow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/data/converter/orderflow.py b/freqtrade/data/converter/orderflow.py index ca19a262237..f0cc726d2ac 100644 --- a/freqtrade/data/converter/orderflow.py +++ b/freqtrade/data/converter/orderflow.py @@ -12,7 +12,7 @@ import numpy as np import pandas as pd -from freqtrade.constants import DEFAULT_ORDERFLOW_COLUMNS +from freqtrade.constants import DEFAULT_ORDERFLOW_COLUMNS, Config from freqtrade.enums import RunMode from freqtrade.exceptions import DependencyException @@ -63,7 +63,7 @@ def _calculate_ohlcv_candle_start_and_end(df: pd.DataFrame, timeframe: str): def populate_dataframe_with_trades( cached_grouped_trades: OrderedDict[Tuple[datetime, datetime], pd.DataFrame], - config, + config: Config, dataframe: pd.DataFrame, trades: pd.DataFrame, ) -> Tuple[pd.DataFrame, OrderedDict[Tuple[datetime, datetime], pd.DataFrame]]: From 6e2aa6b4b81ef64d1ff95c2e2f40ba153cfaa849 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Sep 2024 08:28:12 +0200 Subject: [PATCH 31/93] tests: remove unused imports --- tests/exchange/test_bybit.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/exchange/test_bybit.py b/tests/exchange/test_bybit.py index c72d5ae0dea..f4b8a8ea181 100644 --- a/tests/exchange/test_bybit.py +++ b/tests/exchange/test_bybit.py @@ -1,11 +1,8 @@ from datetime import datetime, timedelta, timezone from unittest.mock import MagicMock -import pytest - from freqtrade.enums.marginmode import MarginMode from freqtrade.enums.tradingmode import TradingMode -from freqtrade.exceptions import OperationalException from tests.conftest import EXMS, get_mock_coro, get_patched_exchange, log_has from tests.exchange.test_exchange import ccxt_exceptionhandlers From 82e30c8519042187a1c7953ec8387e9b946b48d7 Mon Sep 17 00:00:00 2001 From: KingND <81396266+KingND@users.noreply.github.com> Date: Fri, 6 Sep 2024 22:03:17 -0400 Subject: [PATCH 32/93] feat: if a biased_indicator starting with & appears in a lookahead-analysis, caption the table with a note that freqai targets appearing here can be ignored --- .../optimize/analysis/lookahead_helpers.py | 24 +++- tests/optimize/test_lookahead_analysis.py | 114 ++++++++++++++++++ 2 files changed, 135 insertions(+), 3 deletions(-) diff --git a/freqtrade/optimize/analysis/lookahead_helpers.py b/freqtrade/optimize/analysis/lookahead_helpers.py index a8fb1cd3549..aff2083bf45 100644 --- a/freqtrade/optimize/analysis/lookahead_helpers.py +++ b/freqtrade/optimize/analysis/lookahead_helpers.py @@ -19,7 +19,9 @@ class LookaheadAnalysisSubFunctions: @staticmethod def text_table_lookahead_analysis_instances( - config: Dict[str, Any], lookahead_instances: List[LookaheadAnalysis] + config: Dict[str, Any], + lookahead_instances: List[LookaheadAnalysis], + caption: str | None = None, ): headers = [ "filename", @@ -65,7 +67,12 @@ def text_table_lookahead_analysis_instances( ] ) - print_rich_table(data, headers, summary="Lookahead Analysis") + print_rich_table( + data, + headers, + summary="Lookahead Analysis", + table_kwargs={"caption": caption} + ) return data @staticmethod @@ -239,8 +246,19 @@ def start(config: Config): # report the results if lookaheadAnalysis_instances: + caption: str | None = None + if any([ + any([ + indicator.startswith("&") + for indicator in inst.current_analysis.false_indicators + ]) for inst in lookaheadAnalysis_instances + ]): + caption = ( + "Any indicators in 'biased_indicators' which are used within " + "set_freqai_targets() can be ignored." + ) LookaheadAnalysisSubFunctions.text_table_lookahead_analysis_instances( - config, lookaheadAnalysis_instances + config, lookaheadAnalysis_instances, caption=caption ) if config.get("lookahead_analysis_exportfilename") is not None: LookaheadAnalysisSubFunctions.export_to_csv(config, lookaheadAnalysis_instances) diff --git a/tests/optimize/test_lookahead_analysis.py b/tests/optimize/test_lookahead_analysis.py index f7d38b24bcd..387587afb30 100644 --- a/tests/optimize/test_lookahead_analysis.py +++ b/tests/optimize/test_lookahead_analysis.py @@ -133,6 +133,72 @@ def test_lookahead_helper_start(lookahead_conf, mocker) -> None: text_table_mock.reset_mock() +@pytest.mark.parametrize( + "indicators, expected_caption_text", + [ + ( + ["&indicator1", "indicator2"], + "Any indicators in 'biased_indicators' which are used " + "within set_freqai_targets() can be ignored." + ), + ( + ["indicator1", "&indicator2"], + "Any indicators in 'biased_indicators' which are used " + "within set_freqai_targets() can be ignored." + ), + ( + ["&indicator1", "&indicator2"], + "Any indicators in 'biased_indicators' which are used " + "within set_freqai_targets() can be ignored." + ), + ( + ["indicator1", "indicator2"], + None + ), + ( + [], + None + ) + ], + ids=( + "First of two biased indicators starts with '&'", + "Second of two biased indicators starts with '&'", + "Both biased indicators start with '&'", + "No biased indicators start with '&'", + "Empty biased indicators list", + ) +) +def test_lookahead_helper_start__caption_based_on_indicators( + indicators, + expected_caption_text, + lookahead_conf, + mocker +): + """Test that the table caption is only populated if a biased_indicator starts with '&'.""" + + single_mock = MagicMock() + lookahead_analysis = LookaheadAnalysis( + lookahead_conf, + {"name": "strategy_test_v3_with_lookahead_bias"}, + ) + lookahead_analysis.current_analysis.false_indicators = indicators + single_mock.return_value = lookahead_analysis + text_table_mock = MagicMock() + mocker.patch.multiple( + "freqtrade.optimize.analysis.lookahead_helpers.LookaheadAnalysisSubFunctions", + initialize_single_lookahead_analysis=single_mock, + text_table_lookahead_analysis_instances=text_table_mock, + ) + + LookaheadAnalysisSubFunctions.start(lookahead_conf) + + text_table_mock.assert_called_once_with( + lookahead_conf, + [lookahead_analysis], + caption=expected_caption_text + ) + + def test_lookahead_helper_text_table_lookahead_analysis_instances(lookahead_conf): analysis = Analysis() analysis.has_bias = True @@ -199,6 +265,54 @@ def test_lookahead_helper_text_table_lookahead_analysis_instances(lookahead_conf assert len(data) == 3 + +@pytest.mark.parametrize( + "caption", + [ + "", + "A test caption", + None, + False, + ], + ids=( + "Pass empty string", + "Pass non-empty string", + "Pass None", + "Don't pass caption", + ) +) +def test_lookahead_helper_text_table_lookahead_analysis_instances__caption( + caption, + lookahead_conf, + mocker, +): + """Test that the caption is passed in the table kwargs when calling print_rich_table().""" + + print_rich_table_mock = MagicMock() + mocker.patch( + "freqtrade.optimize.analysis.lookahead_helpers.print_rich_table", + print_rich_table_mock, + ) + lookahead_analysis = LookaheadAnalysis( + lookahead_conf, + { + "name": "strategy_test_v3_with_lookahead_bias", + "location": Path(lookahead_conf["strategy_path"], f"{lookahead_conf['strategy']}.py"), + } + ) + kwargs = {} + if caption is not False: + kwargs["caption"] = caption + + LookaheadAnalysisSubFunctions.text_table_lookahead_analysis_instances( + lookahead_conf, [lookahead_analysis], **kwargs + ) + + assert print_rich_table_mock.call_args[-1]["table_kwargs"]["caption"] == ( + caption if caption is not False else None + ) + + def test_lookahead_helper_export_to_csv(lookahead_conf): import pandas as pd From 5f52fc4338c59029f4d176d2103a4bf40ab5bf74 Mon Sep 17 00:00:00 2001 From: KingND <81396266+KingND@users.noreply.github.com> Date: Fri, 6 Sep 2024 22:17:53 -0400 Subject: [PATCH 33/93] feat: update lookahead-analysis doc caveats to include info regarding the false positive on FreqAI targets --- docs/lookahead-analysis.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/lookahead-analysis.md b/docs/lookahead-analysis.md index 90ba7041ada..12dc355fae9 100644 --- a/docs/lookahead-analysis.md +++ b/docs/lookahead-analysis.md @@ -101,3 +101,4 @@ This could lead to a false-negative (the strategy will then be reported as non-b - `lookahead-analysis` has access to everything that backtesting has too. Please don't provoke any configs like enabling position stacking. If you decide to do so, then make doubly sure that you won't ever run out of `max_open_trades` amount and neither leftover money in your wallet. +- `biased_indicators` will falsely flag FreqAI target indicators defined in `set_freqai_targets()` as biased. These are not biased and can safely be ignored. From bb9f64027af88abaca71c9fb72e94fd1c8a8ff49 Mon Sep 17 00:00:00 2001 From: KingND <81396266+KingND@users.noreply.github.com> Date: Fri, 6 Sep 2024 22:32:13 -0400 Subject: [PATCH 34/93] chore: improve language in docs --- docs/lookahead-analysis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/lookahead-analysis.md b/docs/lookahead-analysis.md index 12dc355fae9..1cdf9aaf045 100644 --- a/docs/lookahead-analysis.md +++ b/docs/lookahead-analysis.md @@ -101,4 +101,4 @@ This could lead to a false-negative (the strategy will then be reported as non-b - `lookahead-analysis` has access to everything that backtesting has too. Please don't provoke any configs like enabling position stacking. If you decide to do so, then make doubly sure that you won't ever run out of `max_open_trades` amount and neither leftover money in your wallet. -- `biased_indicators` will falsely flag FreqAI target indicators defined in `set_freqai_targets()` as biased. These are not biased and can safely be ignored. +- In the results table, the `biased_indicators` column will falsely flag FreqAI target indicators defined in `set_freqai_targets()` as biased. These are not biased and can safely be ignored. From c6c65b1799c99e120c4a58a024ed838cae02534d Mon Sep 17 00:00:00 2001 From: KingND <81396266+KingND@users.noreply.github.com> Date: Fri, 6 Sep 2024 22:38:56 -0400 Subject: [PATCH 35/93] chore: flake8 --- tests/optimize/test_lookahead_analysis.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/optimize/test_lookahead_analysis.py b/tests/optimize/test_lookahead_analysis.py index 387587afb30..eb64ad42dd0 100644 --- a/tests/optimize/test_lookahead_analysis.py +++ b/tests/optimize/test_lookahead_analysis.py @@ -265,7 +265,6 @@ def test_lookahead_helper_text_table_lookahead_analysis_instances(lookahead_conf assert len(data) == 3 - @pytest.mark.parametrize( "caption", [ From 53cab5074b0f94800e62c400dee7750b9121f986 Mon Sep 17 00:00:00 2001 From: KingND <81396266+KingND@users.noreply.github.com> Date: Fri, 6 Sep 2024 22:42:44 -0400 Subject: [PATCH 36/93] chore: refactor and cleanup tests --- tests/optimize/test_lookahead_analysis.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/tests/optimize/test_lookahead_analysis.py b/tests/optimize/test_lookahead_analysis.py index eb64ad42dd0..dd036cdc998 100644 --- a/tests/optimize/test_lookahead_analysis.py +++ b/tests/optimize/test_lookahead_analysis.py @@ -13,6 +13,12 @@ from tests.conftest import EXMS, get_args, log_has_re, patch_exchange +IGNORE_BIASED_INDICATORS_CAPTION = ( + "Any indicators in 'biased_indicators' which are used within " + "set_freqai_targets() can be ignored." +) + + @pytest.fixture def lookahead_conf(default_conf_usdt, tmp_path): default_conf_usdt["user_data_dir"] = tmp_path @@ -138,18 +144,15 @@ def test_lookahead_helper_start(lookahead_conf, mocker) -> None: [ ( ["&indicator1", "indicator2"], - "Any indicators in 'biased_indicators' which are used " - "within set_freqai_targets() can be ignored." + IGNORE_BIASED_INDICATORS_CAPTION, ), ( ["indicator1", "&indicator2"], - "Any indicators in 'biased_indicators' which are used " - "within set_freqai_targets() can be ignored." + IGNORE_BIASED_INDICATORS_CAPTION, ), ( ["&indicator1", "&indicator2"], - "Any indicators in 'biased_indicators' which are used " - "within set_freqai_targets() can be ignored." + IGNORE_BIASED_INDICATORS_CAPTION, ), ( ["indicator1", "indicator2"], @@ -158,7 +161,7 @@ def test_lookahead_helper_start(lookahead_conf, mocker) -> None: ( [], None - ) + ), ], ids=( "First of two biased indicators starts with '&'", @@ -166,7 +169,7 @@ def test_lookahead_helper_start(lookahead_conf, mocker) -> None: "Both biased indicators start with '&'", "No biased indicators start with '&'", "Empty biased indicators list", - ) + ), ) def test_lookahead_helper_start__caption_based_on_indicators( indicators, @@ -278,7 +281,7 @@ def test_lookahead_helper_text_table_lookahead_analysis_instances(lookahead_conf "Pass non-empty string", "Pass None", "Don't pass caption", - ) + ), ) def test_lookahead_helper_text_table_lookahead_analysis_instances__caption( caption, From 69678574d4e3421ed3fe697a58b9624091f03242 Mon Sep 17 00:00:00 2001 From: KingND <81396266+KingND@users.noreply.github.com> Date: Sat, 7 Sep 2024 16:01:52 -0400 Subject: [PATCH 37/93] fix: support python 3.9 union type hinting --- freqtrade/optimize/analysis/lookahead_helpers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/optimize/analysis/lookahead_helpers.py b/freqtrade/optimize/analysis/lookahead_helpers.py index aff2083bf45..b3cba9ed438 100644 --- a/freqtrade/optimize/analysis/lookahead_helpers.py +++ b/freqtrade/optimize/analysis/lookahead_helpers.py @@ -1,7 +1,7 @@ import logging import time from pathlib import Path -from typing import Any, Dict, List +from typing import Any, Dict, List, Union import pandas as pd from rich.text import Text @@ -21,7 +21,7 @@ class LookaheadAnalysisSubFunctions: def text_table_lookahead_analysis_instances( config: Dict[str, Any], lookahead_instances: List[LookaheadAnalysis], - caption: str | None = None, + caption: Union[str, None] = None, ): headers = [ "filename", @@ -246,7 +246,7 @@ def start(config: Config): # report the results if lookaheadAnalysis_instances: - caption: str | None = None + caption: Union[str, None] = None if any([ any([ indicator.startswith("&") From f970454cb49d6d9f213e4114a960de2f4c2516f6 Mon Sep 17 00:00:00 2001 From: KingND <81396266+KingND@users.noreply.github.com> Date: Sun, 8 Sep 2024 13:57:48 -0400 Subject: [PATCH 38/93] chore: ruff format --- .../optimize/analysis/lookahead_helpers.py | 22 ++++++++++--------- tests/optimize/test_lookahead_analysis.py | 21 +++++------------- 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/freqtrade/optimize/analysis/lookahead_helpers.py b/freqtrade/optimize/analysis/lookahead_helpers.py index b3cba9ed438..730f9fd7293 100644 --- a/freqtrade/optimize/analysis/lookahead_helpers.py +++ b/freqtrade/optimize/analysis/lookahead_helpers.py @@ -68,10 +68,7 @@ def text_table_lookahead_analysis_instances( ) print_rich_table( - data, - headers, - summary="Lookahead Analysis", - table_kwargs={"caption": caption} + data, headers, summary="Lookahead Analysis", table_kwargs={"caption": caption} ) return data @@ -247,12 +244,17 @@ def start(config: Config): # report the results if lookaheadAnalysis_instances: caption: Union[str, None] = None - if any([ - any([ - indicator.startswith("&") - for indicator in inst.current_analysis.false_indicators - ]) for inst in lookaheadAnalysis_instances - ]): + if any( + [ + any( + [ + indicator.startswith("&") + for indicator in inst.current_analysis.false_indicators + ] + ) + for inst in lookaheadAnalysis_instances + ] + ): caption = ( "Any indicators in 'biased_indicators' which are used within " "set_freqai_targets() can be ignored." diff --git a/tests/optimize/test_lookahead_analysis.py b/tests/optimize/test_lookahead_analysis.py index dd036cdc998..67c83762a20 100644 --- a/tests/optimize/test_lookahead_analysis.py +++ b/tests/optimize/test_lookahead_analysis.py @@ -154,14 +154,8 @@ def test_lookahead_helper_start(lookahead_conf, mocker) -> None: ["&indicator1", "&indicator2"], IGNORE_BIASED_INDICATORS_CAPTION, ), - ( - ["indicator1", "indicator2"], - None - ), - ( - [], - None - ), + (["indicator1", "indicator2"], None), + ([], None), ], ids=( "First of two biased indicators starts with '&'", @@ -172,10 +166,7 @@ def test_lookahead_helper_start(lookahead_conf, mocker) -> None: ), ) def test_lookahead_helper_start__caption_based_on_indicators( - indicators, - expected_caption_text, - lookahead_conf, - mocker + indicators, expected_caption_text, lookahead_conf, mocker ): """Test that the table caption is only populated if a biased_indicator starts with '&'.""" @@ -196,9 +187,7 @@ def test_lookahead_helper_start__caption_based_on_indicators( LookaheadAnalysisSubFunctions.start(lookahead_conf) text_table_mock.assert_called_once_with( - lookahead_conf, - [lookahead_analysis], - caption=expected_caption_text + lookahead_conf, [lookahead_analysis], caption=expected_caption_text ) @@ -300,7 +289,7 @@ def test_lookahead_helper_text_table_lookahead_analysis_instances__caption( { "name": "strategy_test_v3_with_lookahead_bias", "location": Path(lookahead_conf["strategy_path"], f"{lookahead_conf['strategy']}.py"), - } + }, ) kwargs = {} if caption is not False: From df9669ba2c161f87f65f21bd5ca75551df7e2b2b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 03:43:50 +0000 Subject: [PATCH 39/93] chore(deps-dev): bump the types group with 2 updates Bumps the types group with 2 updates: [types-requests](https://github.com/python/typeshed) and [types-python-dateutil](https://github.com/python/typeshed). Updates `types-requests` from 2.32.0.20240712 to 2.32.0.20240907 - [Commits](https://github.com/python/typeshed/commits) Updates `types-python-dateutil` from 2.9.0.20240821 to 2.9.0.20240906 - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-requests dependency-type: direct:development update-type: version-update:semver-patch dependency-group: types - dependency-name: types-python-dateutil dependency-type: direct:development update-type: version-update:semver-patch dependency-group: types ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index b0a46db7167..06b770232d8 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -27,6 +27,6 @@ nbconvert==7.16.4 # mypy types types-cachetools==5.5.0.20240820 types-filelock==3.2.7 -types-requests==2.32.0.20240712 +types-requests==2.32.0.20240907 types-tabulate==0.9.0.20240106 -types-python-dateutil==2.9.0.20240821 +types-python-dateutil==2.9.0.20240906 From 9856c2cfc48e632dc4edc9d6a8003f573c255c75 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 03:44:57 +0000 Subject: [PATCH 40/93] chore(deps): bump pydantic from 2.8.2 to 2.9.0 Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.8.2 to 2.9.0. - [Release notes](https://github.com/pydantic/pydantic/releases) - [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md) - [Commits](https://github.com/pydantic/pydantic/compare/v2.8.2...v2.9.0) --- updated-dependencies: - dependency-name: pydantic dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 31d663b151b..390d942956a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -42,7 +42,7 @@ sdnotify==0.3.2 # API Server fastapi==0.112.2 -pydantic==2.8.2 +pydantic==2.9.0 uvicorn==0.30.6 pyjwt==2.9.0 aiofiles==24.1.0 From 47358a822964c120e9170b58d3ab4c9d7427e5d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 03:45:03 +0000 Subject: [PATCH 41/93] chore(deps): bump catboost from 1.2.5 to 1.2.7 Bumps [catboost](https://github.com/catboost/catboost) from 1.2.5 to 1.2.7. - [Release notes](https://github.com/catboost/catboost/releases) - [Changelog](https://github.com/catboost/catboost/blob/master/RELEASE.md) - [Commits](https://github.com/catboost/catboost/compare/v1.2.5...v1.2.7) --- updated-dependencies: - dependency-name: catboost dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-freqai.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-freqai.txt b/requirements-freqai.txt index 0db247289ed..1731ac0546b 100644 --- a/requirements-freqai.txt +++ b/requirements-freqai.txt @@ -5,7 +5,7 @@ # Required for freqai scikit-learn==1.5.1 joblib==1.4.2 -catboost==1.2.5; 'arm' not in platform_machine +catboost==1.2.7; 'arm' not in platform_machine # Pin Matplotlib - it's depended on by catboost # Temporary downgrade of matplotlib due to https://github.com/matplotlib/matplotlib/issues/28551 matplotlib==3.9.2 From ccf93cfdcdb7f89359410bb4342b1cd31cf52c8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 03:45:14 +0000 Subject: [PATCH 42/93] chore(deps): bump torch from 2.2.2 to 2.4.1 Bumps [torch](https://github.com/pytorch/pytorch) from 2.2.2 to 2.4.1. - [Release notes](https://github.com/pytorch/pytorch/releases) - [Changelog](https://github.com/pytorch/pytorch/blob/main/RELEASE.md) - [Commits](https://github.com/pytorch/pytorch/compare/v2.2.2...v2.4.1) --- updated-dependencies: - dependency-name: torch dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-freqai-rl.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-freqai-rl.txt b/requirements-freqai-rl.txt index 9b808f66f3a..c9db23d9638 100644 --- a/requirements-freqai-rl.txt +++ b/requirements-freqai-rl.txt @@ -3,7 +3,7 @@ # Required for freqai-rl torch==2.2.2; sys_platform == 'darwin' and platform_machine == 'x86_64' -torch==2.4.0; sys_platform != 'darwin' or platform_machine != 'x86_64' +torch==2.4.1; sys_platform != 'darwin' or platform_machine != 'x86_64' gymnasium==0.29.1 stable_baselines3==2.3.2 sb3_contrib>=2.2.1 From 699be03bb73d492acb898521c181a53afcd9768a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 03:45:29 +0000 Subject: [PATCH 43/93] chore(deps): bump filelock from 3.15.4 to 3.16.0 Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.15.4 to 3.16.0. - [Release notes](https://github.com/tox-dev/py-filelock/releases) - [Changelog](https://github.com/tox-dev/filelock/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/py-filelock/compare/3.15.4...3.16.0) --- updated-dependencies: - dependency-name: filelock dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-hyperopt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 3391d8c68a2..0daa19128ae 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -6,4 +6,4 @@ scipy==1.14.1; python_version >= "3.10" scipy==1.13.1; python_version < "3.10" scikit-learn==1.5.1 ft-scikit-optimize==0.9.2 -filelock==3.15.4 +filelock==3.16.0 From d099f30a3420d329da728bd9c3a1982f1864f6d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 03:45:51 +0000 Subject: [PATCH 44/93] chore(deps-dev): bump ruff from 0.6.3 to 0.6.4 Bumps [ruff](https://github.com/astral-sh/ruff) from 0.6.3 to 0.6.4. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.6.3...0.6.4) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index b0a46db7167..3715965389b 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,7 +7,7 @@ -r docs/requirements-docs.txt coveralls==4.0.1 -ruff==0.6.3 +ruff==0.6.4 mypy==1.11.2 pre-commit==3.8.0 pytest==8.3.2 From b0976031aedc2dcdc04cc5694a21443c36e837d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 03:46:05 +0000 Subject: [PATCH 45/93] chore(deps): bump ccxt from 4.3.93 to 4.3.98 Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.3.93 to 4.3.98. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md) - [Commits](https://github.com/ccxt/ccxt/compare/4.3.93...4.3.98) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 31d663b151b..91ae1509a38 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ bottleneck==1.4.0 numexpr==2.10.1 pandas-ta==0.3.14b -ccxt==4.3.93 +ccxt==4.3.98 cryptography==42.0.8; platform_machine == 'armv7l' cryptography==43.0.1; platform_machine != 'armv7l' aiohttp==3.10.5 From b7bda2355d94f79081b95ee803c4703eadccc088 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 03:46:24 +0000 Subject: [PATCH 46/93] chore(deps): bump sqlalchemy from 2.0.32 to 2.0.34 Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 2.0.32 to 2.0.34. - [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases) - [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES.rst) - [Commits](https://github.com/sqlalchemy/sqlalchemy/commits) --- updated-dependencies: - dependency-name: sqlalchemy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 31d663b151b..0aeda99480b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ ccxt==4.3.93 cryptography==42.0.8; platform_machine == 'armv7l' cryptography==43.0.1; platform_machine != 'armv7l' aiohttp==3.10.5 -SQLAlchemy==2.0.32 +SQLAlchemy==2.0.34 python-telegram-bot==21.5 # can't be hard-pinned due to telegram-bot pinning httpx with ~ httpx>=0.24.1 From 05af6df5363832579854dda96f5ff2ca2fe7a992 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 03:59:29 +0000 Subject: [PATCH 47/93] chore(deps): bump peter-evans/create-pull-request from 6 to 7 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 6 to 7. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/v6...v7) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/binance-lev-tier-update.yml | 2 +- .github/workflows/pre-commit-update.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/binance-lev-tier-update.yml b/.github/workflows/binance-lev-tier-update.yml index 2e0a3d3b285..844a6c8f51f 100644 --- a/.github/workflows/binance-lev-tier-update.yml +++ b/.github/workflows/binance-lev-tier-update.yml @@ -32,7 +32,7 @@ jobs: run: python build_helpers/binance_update_lev_tiers.py - - uses: peter-evans/create-pull-request@v6 + - uses: peter-evans/create-pull-request@v7 with: token: ${{ secrets.REPO_SCOPED_TOKEN }} add-paths: freqtrade/exchange/binance_leverage_tiers.json diff --git a/.github/workflows/pre-commit-update.yml b/.github/workflows/pre-commit-update.yml index d30fdd1bfe3..5d71f513f2a 100644 --- a/.github/workflows/pre-commit-update.yml +++ b/.github/workflows/pre-commit-update.yml @@ -26,7 +26,7 @@ jobs: - name: Run auto-update run: pre-commit autoupdate - - uses: peter-evans/create-pull-request@v6 + - uses: peter-evans/create-pull-request@v7 with: token: ${{ secrets.REPO_SCOPED_TOKEN }} add-paths: .pre-commit-config.yaml From 621be1139518934dd0c615d75b8f72a90570ffe3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 03:59:34 +0000 Subject: [PATCH 48/93] chore(deps): bump pypa/gh-action-pypi-publish from 1.10.0 to 1.10.1 Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.10.0 to 1.10.1. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.10.0...v1.10.1) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ec36161bfe..d45841a28f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -537,12 +537,12 @@ jobs: - name: Publish to PyPI (Test) - uses: pypa/gh-action-pypi-publish@v1.10.0 + uses: pypa/gh-action-pypi-publish@v1.10.1 with: repository-url: https://test.pypi.org/legacy/ - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@v1.10.0 + uses: pypa/gh-action-pypi-publish@v1.10.1 deploy-docker: From 9b97be4aa4f68ea97bfdf6f10d900e51910ca566 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Sep 2024 06:44:35 +0200 Subject: [PATCH 49/93] Bump pre-commit dependencies --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3e1d9fa3430..9da692bcef1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,9 +16,9 @@ repos: additional_dependencies: - types-cachetools==5.5.0.20240820 - types-filelock==3.2.7 - - types-requests==2.32.0.20240712 + - types-requests==2.32.0.20240907 - types-tabulate==0.9.0.20240106 - - types-python-dateutil==2.9.0.20240821 + - types-python-dateutil==2.9.0.20240906 - SQLAlchemy==2.0.32 # stages: [push] From 7aa7027a3452839debf484216eeadba00b95812b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 06:55:07 +0000 Subject: [PATCH 50/93] chore(deps): bump fastapi from 0.112.2 to 0.114.0 Bumps [fastapi](https://github.com/fastapi/fastapi) from 0.112.2 to 0.114.0. - [Release notes](https://github.com/fastapi/fastapi/releases) - [Commits](https://github.com/fastapi/fastapi/compare/0.112.2...0.114.0) --- updated-dependencies: - dependency-name: fastapi dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 390d942956a..0dce545d2f2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -41,7 +41,7 @@ orjson==3.10.7 sdnotify==0.3.2 # API Server -fastapi==0.112.2 +fastapi==0.114.0 pydantic==2.9.0 uvicorn==0.30.6 pyjwt==2.9.0 From 2c17551b2750c1b2fd132957ce349052427b10c5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Sep 2024 10:15:18 +0200 Subject: [PATCH 51/93] chore: bump sqlalchemy in mypy additional deps --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3e1d9fa3430..328387ead74 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,7 +19,7 @@ repos: - types-requests==2.32.0.20240712 - types-tabulate==0.9.0.20240106 - types-python-dateutil==2.9.0.20240821 - - SQLAlchemy==2.0.32 + - SQLAlchemy==2.0.34 # stages: [push] - repo: https://github.com/pycqa/isort From 9742216479b3deef0812483efa4969992d0e76a2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Sep 2024 18:23:07 +0200 Subject: [PATCH 52/93] chore: run ruff-format on pre-commit --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6fe2f79d2ea..66c81b3de2c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,6 +34,7 @@ repos: rev: 'v0.6.3' hooks: - id: ruff + - id: ruff-format - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.6.0 From ae155c78c26d26fc778201ad800ff94acf615ac7 Mon Sep 17 00:00:00 2001 From: dxbstyle Date: Mon, 9 Sep 2024 21:29:49 +0200 Subject: [PATCH 53/93] added check --- freqtrade/exchange/kraken.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py index 7f3346cfe0a..553e61ff8c4 100644 --- a/freqtrade/exchange/kraken.py +++ b/freqtrade/exchange/kraken.py @@ -78,6 +78,7 @@ def get_balances(self) -> CcxtBalances: # x["side"], x["amount"], ) for x in orders + if x["price"] is not None ] for bal in balances: if not isinstance(balances[bal], dict): From 01da36f984f8bab317a1f33cfd5dc49e90da46bf Mon Sep 17 00:00:00 2001 From: xmatthias <5024695+xmatthias@users.noreply.github.com> Date: Tue, 10 Sep 2024 03:03:23 +0000 Subject: [PATCH 54/93] chore: update pre-commit hooks --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 66c81b3de2c..3fcde5a369a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -31,7 +31,7 @@ repos: - repo: https://github.com/charliermarsh/ruff-pre-commit # Ruff version. - rev: 'v0.6.3' + rev: 'v0.6.4' hooks: - id: ruff - id: ruff-format From 98e08df807325aa2f4494ded2a0d1e2ea44faf39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 04:07:26 +0000 Subject: [PATCH 55/93] chore(deps): bump python Bumps python from 3.12.5-slim-bookworm to 3.12.6-slim-bookworm. --- updated-dependencies: - dependency-name: python dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index fbe1de16509..bc2fc463564 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.12.5-slim-bookworm as base +FROM python:3.12.6-slim-bookworm as base # Setup env ENV LANG C.UTF-8 From 95fa7083a9989cb2551f01894493fdd29e32359a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 04:07:33 +0000 Subject: [PATCH 56/93] chore(deps): bump python in /docker Bumps python from 3.11.9-slim-bookworm to 3.12.6-slim-bookworm. --- updated-dependencies: - dependency-name: python dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- docker/Dockerfile.armhf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile.armhf b/docker/Dockerfile.armhf index ed3c5fbdea0..2fdf1606c64 100644 --- a/docker/Dockerfile.armhf +++ b/docker/Dockerfile.armhf @@ -1,4 +1,4 @@ -FROM python:3.11.9-slim-bookworm as base +FROM python:3.12.6-slim-bookworm as base # Setup env ENV LANG C.UTF-8 From 8c1b119e8424f7e5ad565f96ec10010738c610f9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 10 Sep 2024 06:25:17 +0200 Subject: [PATCH 57/93] chore: rpi image should remain on 3.11 series --- docker/Dockerfile.armhf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile.armhf b/docker/Dockerfile.armhf index 2fdf1606c64..1a6d52ad4b1 100644 --- a/docker/Dockerfile.armhf +++ b/docker/Dockerfile.armhf @@ -1,4 +1,4 @@ -FROM python:3.12.6-slim-bookworm as base +FROM python:3.11.10-slim-bookworm as base # Setup env ENV LANG C.UTF-8 From 4765656f87d5e4411218e01923d9742671e87076 Mon Sep 17 00:00:00 2001 From: Anuj Jain Date: Tue, 10 Sep 2024 15:21:56 +0530 Subject: [PATCH 58/93] Add filter for entry and exit only parameter --- freqtrade/commands/arguments.py | 2 ++ freqtrade/commands/cli_options.py | 6 ++++ freqtrade/configuration/configuration.py | 2 ++ freqtrade/data/entryexitanalysis.py | 37 ++++++++++++++++++++---- 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 62a79b0e85e..0bc3bc7f733 100755 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -228,6 +228,8 @@ "enter_reason_list", "exit_reason_list", "indicator_list", + "entry_only", + "exit_only", "timerange", "analysis_rejected", "analysis_to_csv", diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 54e139443de..d279569c506 100755 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -719,6 +719,12 @@ def __init__(self, *args, **kwargs): nargs="+", default=[], ), + "entry_only": Arg( + "--entry-only", help=("Only analyze entry signals."), action="store_true", default=False + ), + "exit_only": Arg( + "--exit-only", help=("Only analyze exit signals."), action="store_true", default=False + ), "analysis_rejected": Arg( "--rejected-signals", help="Analyse rejected signals", diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 1bbc8486170..0a99f904433 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -407,6 +407,8 @@ def _process_analyze_options(self, config: Config) -> None: ("enter_reason_list", "Analysis enter tag list: {}"), ("exit_reason_list", "Analysis exit tag list: {}"), ("indicator_list", "Analysis indicator list: {}"), + ("entry_only", "Only analyze entry signals: {}"), + ("exit_only", "Only analyze exit signals: {}"), ("timerange", "Filter trades by timerange: {}"), ("analysis_rejected", "Analyse rejected signals: {}"), ("analysis_to_csv", "Store analysis tables to CSV: {}"), diff --git a/freqtrade/data/entryexitanalysis.py b/freqtrade/data/entryexitanalysis.py index 8077e104a06..f7ab7836f53 100644 --- a/freqtrade/data/entryexitanalysis.py +++ b/freqtrade/data/entryexitanalysis.py @@ -263,6 +263,8 @@ def print_results( exit_df: pd.DataFrame, analysis_groups: List[str], indicator_list: List[str], + entry_only: bool, + exit_only: bool, csv_path: Path, rejected_signals=None, to_csv=False, @@ -288,7 +290,7 @@ def print_results( if ind in res_df: available_inds.append(ind) - merged_df = _merge_dfs(res_df, exit_df, available_inds) + merged_df = _merge_dfs(res_df, exit_df, available_inds, entry_only, exit_only) _print_table( merged_df, @@ -302,16 +304,30 @@ def print_results( print("\\No trades to show") -def _merge_dfs(entry_df, exit_df, available_inds): +def _merge_dfs( + entry_df: pd.DataFrame, + exit_df: pd.DataFrame, + available_inds: List[str], + entry_only: bool, + exit_only: bool, +): merge_on = ["pair", "open_date"] signal_wide_indicators = list(set(available_inds) - set(BT_DATA_COLUMNS)) - columns_to_keep = merge_on + ["enter_reason", "exit_reason"] + available_inds + columns_to_keep = merge_on + ["enter_reason", "exit_reason"] + + if exit_df is None or exit_df.empty or entry_only is True: + return entry_df[columns_to_keep + available_inds] - if exit_df is None or exit_df.empty: - return entry_df[columns_to_keep] + if exit_only is True: + return pd.merge( + entry_df[columns_to_keep], + exit_df[merge_on + signal_wide_indicators], + on=merge_on, + suffixes=(" (entry)", " (exit)"), + ) return pd.merge( - entry_df[columns_to_keep], + entry_df[columns_to_keep + available_inds], exit_df[merge_on + signal_wide_indicators], on=merge_on, suffixes=(" (entry)", " (exit)"), @@ -343,9 +359,16 @@ def process_entry_exit_reasons(config: Config): enter_reason_list = config.get("enter_reason_list", ["all"]) exit_reason_list = config.get("exit_reason_list", ["all"]) indicator_list = config.get("indicator_list", []) + entry_only = config.get("entry_only", False) + exit_only = config.get("exit_only", False) do_rejected = config.get("analysis_rejected", False) to_csv = config.get("analysis_to_csv", False) csv_path = Path(config.get("analysis_csv_path", config["exportfilename"])) + + if entry_only is True and exit_only is True: + raise OperationalException( + "Cannot use --entry-only and --exit-only at the same time. Please choose one." + ) if to_csv and not csv_path.is_dir(): raise OperationalException(f"Specified directory {csv_path} does not exist.") @@ -400,6 +423,8 @@ def process_entry_exit_reasons(config: Config): exit_df, analysis_groups, indicator_list, + entry_only, + exit_only, rejected_signals=rej_df, to_csv=to_csv, csv_path=csv_path, From f1df7e9bdc3306bea81df8b019dee6c30153e7b4 Mon Sep 17 00:00:00 2001 From: colorfulgray0 Date: Wed, 11 Sep 2024 11:33:38 +0800 Subject: [PATCH 59/93] chore: remove redundant method --- freqtrade/rpc/telegram.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 22b57462189..46939daedf6 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -1274,7 +1274,7 @@ async def _force_exit(self, update: Update, context: CallbackContext) -> None: InlineKeyboardButton(text=trade[1], callback_data=f"force_exit__{trade[0]}") for trade in trades ] - buttons_aligned = self._layout_inline_keyboard_onecol(trade_buttons) + buttons_aligned = self._layout_inline_keyboard(trade_buttons, cols=1) buttons_aligned.append( [InlineKeyboardButton(text="Cancel", callback_data="force_exit__cancel")] @@ -1348,12 +1348,6 @@ def _layout_inline_keyboard( ) -> List[List[InlineKeyboardButton]]: return [buttons[i : i + cols] for i in range(0, len(buttons), cols)] - @staticmethod - def _layout_inline_keyboard_onecol( - buttons: List[InlineKeyboardButton], cols=1 - ) -> List[List[InlineKeyboardButton]]: - return [buttons[i : i + cols] for i in range(0, len(buttons), cols)] - @authorized_only async def _force_enter( self, update: Update, context: CallbackContext, order_side: SignalDirection From addd27faf816afb00a667e131a48f4d27c246d5e Mon Sep 17 00:00:00 2001 From: Anuj Jain Date: Wed, 11 Sep 2024 15:33:26 +0530 Subject: [PATCH 60/93] Update tests and docs --- docs/advanced-backtesting.md | 19 ++ tests/data/test_entryexitanalysis.py | 304 +++++++++++++++++++++++++++ 2 files changed, 323 insertions(+) diff --git a/docs/advanced-backtesting.md b/docs/advanced-backtesting.md index b97db79c527..40584a65659 100644 --- a/docs/advanced-backtesting.md +++ b/docs/advanced-backtesting.md @@ -149,6 +149,25 @@ to distinguish the values at the entry and exit points of the trade. `profit_ratio`, `profit_abs`, `exit_reason`,`initial_stop_loss_abs`, `initial_stop_loss_ratio`, `stop_loss_abs`, `stop_loss_ratio`, `min_rate`, `max_rate`, `is_open`, `enter_tag`, `leverage`, `is_short`, `open_timestamp`, `close_timestamp` and `orders` +#### Filtering Indicators Based on Entry or Exit Signals + +The `--indicator-list` option, by default, displays indicator values for both entry and exit signals. To filter the indicator values exclusively for entry signals, you can use the `--entry-only` argument. Similarly, to display indicator values only at exit signals, use the `--exit-only` argument. + +Example: Display indicator values at entry signals: + +```bash +freqtrade backtesting-analysis -c user_data/config.json --analysis-groups 0 --indicator-list chikou_span tenkan_sen --entry-only +``` + +Example: Display indicator values at exit signals: + +```bash +freqtrade backtesting-analysis -c user_data/config.json --analysis-groups 0 --indicator-list chikou_span tenkan_sen --exit-only +``` + +!!! note + When using these filters, the indicator names will not be suffixed with `(entry)` or `(exit)`. + ### Filtering the trade output by date To show only trades between dates within your backtested timerange, supply the usual `timerange` option in `YYYYMMDD-[YYYYMMDD]` format: diff --git a/tests/data/test_entryexitanalysis.py b/tests/data/test_entryexitanalysis.py index 374b84fc745..509c9b92c07 100644 --- a/tests/data/test_entryexitanalysis.py +++ b/tests/data/test_entryexitanalysis.py @@ -7,6 +7,7 @@ from freqtrade.commands.analyze_commands import start_analysis_entries_exits from freqtrade.commands.optimize_commands import start_backtesting from freqtrade.enums import ExitType +from freqtrade.exceptions import OperationalException from freqtrade.optimize.backtesting import Backtesting from tests.conftest import get_args, patch_exchange, patched_configuration_load_config_file @@ -256,3 +257,306 @@ def test_backtest_analysis_on_entry_and_rejected_signals_nomock( start_analysis_entries_exits(args) captured = capsys.readouterr() assert "no rejected signals" in captured.out + + +def test_backtest_analysis_with_invalid_config( + default_conf, mocker, caplog, testdatadir, user_dir, capsys +): + caplog.set_level(logging.INFO) + (user_dir / "backtest_results").mkdir(parents=True, exist_ok=True) + + default_conf.update( + { + "use_exit_signal": True, + "exit_profit_only": False, + "exit_profit_offset": 0.0, + "ignore_roi_if_entry_signal": False, + } + ) + patch_exchange(mocker) + result1 = pd.DataFrame( + { + "pair": ["ETH/BTC", "LTC/BTC", "ETH/BTC", "LTC/BTC"], + "profit_ratio": [0.025, 0.05, -0.1, -0.05], + "profit_abs": [0.5, 2.0, -4.0, -2.0], + "open_date": pd.to_datetime( + [ + "2018-01-29 18:40:00", + "2018-01-30 03:30:00", + "2018-01-30 08:10:00", + "2018-01-31 13:30:00", + ], + utc=True, + ), + "close_date": pd.to_datetime( + [ + "2018-01-29 20:45:00", + "2018-01-30 05:35:00", + "2018-01-30 09:10:00", + "2018-01-31 15:00:00", + ], + utc=True, + ), + "trade_duration": [235, 40, 60, 90], + "is_open": [False, False, False, False], + "stake_amount": [0.01, 0.01, 0.01, 0.01], + "open_rate": [0.104445, 0.10302485, 0.10302485, 0.10302485], + "close_rate": [0.104969, 0.103541, 0.102041, 0.102541], + "is_short": [False, False, False, False], + "enter_tag": [ + "enter_tag_long_a", + "enter_tag_long_b", + "enter_tag_long_a", + "enter_tag_long_b", + ], + "exit_reason": [ + ExitType.ROI.value, + ExitType.EXIT_SIGNAL.value, + ExitType.STOP_LOSS.value, + ExitType.TRAILING_STOP_LOSS.value, + ], + } + ) + + backtestmock = MagicMock( + side_effect=[ + { + "results": result1, + "config": default_conf, + "locks": [], + "rejected_signals": 20, + "timedout_entry_orders": 0, + "timedout_exit_orders": 0, + "canceled_trade_entries": 0, + "canceled_entry_orders": 0, + "replaced_entry_orders": 0, + "final_balance": 1000, + } + ] + ) + mocker.patch( + "freqtrade.plugins.pairlistmanager.PairListManager.whitelist", + PropertyMock(return_value=["ETH/BTC", "LTC/BTC", "DASH/BTC"]), + ) + mocker.patch("freqtrade.optimize.backtesting.Backtesting.backtest", backtestmock) + + patched_configuration_load_config_file(mocker, default_conf) + + args = [ + "backtesting", + "--config", + "config.json", + "--datadir", + str(testdatadir), + "--user-data-dir", + str(user_dir), + "--timeframe", + "5m", + "--timerange", + "1515560100-1517287800", + "--export", + "signals", + "--cache", + "none", + ] + args = get_args(args) + start_backtesting(args) + + captured = capsys.readouterr() + assert "BACKTESTING REPORT" in captured.out + assert "EXIT REASON STATS" in captured.out + assert "LEFT OPEN TRADES REPORT" in captured.out + + base_args = [ + "backtesting-analysis", + "--config", + "config.json", + "--datadir", + str(testdatadir), + "--user-data-dir", + str(user_dir), + ] + + # test with both entry and exit only arguments + args = get_args( + base_args + + [ + "--analysis-groups", + "0", + "--indicator-list", + "close", + "rsi", + "profit_abs", + "--entry-only", + "--exit-only", + ] + ) + with pytest.raises( + OperationalException, + match=r"Cannot use --entry-only and --exit-only at the same time. Please choose one.", + ): + start_analysis_entries_exits(args) + + +def test_backtest_analysis_on_entry_and_rejected_signals_only_entry_signals( + default_conf, mocker, caplog, testdatadir, user_dir, capsys +): + caplog.set_level(logging.INFO) + (user_dir / "backtest_results").mkdir(parents=True, exist_ok=True) + + default_conf.update( + { + "use_exit_signal": True, + "exit_profit_only": False, + "exit_profit_offset": 0.0, + "ignore_roi_if_entry_signal": False, + } + ) + patch_exchange(mocker) + result1 = pd.DataFrame( + { + "pair": ["ETH/BTC", "LTC/BTC", "ETH/BTC", "LTC/BTC"], + "profit_ratio": [0.025, 0.05, -0.1, -0.05], + "profit_abs": [0.5, 2.0, -4.0, -2.0], + "open_date": pd.to_datetime( + [ + "2018-01-29 18:40:00", + "2018-01-30 03:30:00", + "2018-01-30 08:10:00", + "2018-01-31 13:30:00", + ], + utc=True, + ), + "close_date": pd.to_datetime( + [ + "2018-01-29 20:45:00", + "2018-01-30 05:35:00", + "2018-01-30 09:10:00", + "2018-01-31 15:00:00", + ], + utc=True, + ), + "trade_duration": [235, 40, 60, 90], + "is_open": [False, False, False, False], + "stake_amount": [0.01, 0.01, 0.01, 0.01], + "open_rate": [0.104445, 0.10302485, 0.10302485, 0.10302485], + "close_rate": [0.104969, 0.103541, 0.102041, 0.102541], + "is_short": [False, False, False, False], + "enter_tag": [ + "enter_tag_long_a", + "enter_tag_long_b", + "enter_tag_long_a", + "enter_tag_long_b", + ], + "exit_reason": [ + ExitType.ROI.value, + ExitType.EXIT_SIGNAL.value, + ExitType.STOP_LOSS.value, + ExitType.TRAILING_STOP_LOSS.value, + ], + } + ) + + backtestmock = MagicMock( + side_effect=[ + { + "results": result1, + "config": default_conf, + "locks": [], + "rejected_signals": 20, + "timedout_entry_orders": 0, + "timedout_exit_orders": 0, + "canceled_trade_entries": 0, + "canceled_entry_orders": 0, + "replaced_entry_orders": 0, + "final_balance": 1000, + } + ] + ) + mocker.patch( + "freqtrade.plugins.pairlistmanager.PairListManager.whitelist", + PropertyMock(return_value=["ETH/BTC", "LTC/BTC", "DASH/BTC"]), + ) + mocker.patch("freqtrade.optimize.backtesting.Backtesting.backtest", backtestmock) + + patched_configuration_load_config_file(mocker, default_conf) + + args = [ + "backtesting", + "--config", + "config.json", + "--datadir", + str(testdatadir), + "--user-data-dir", + str(user_dir), + "--timeframe", + "5m", + "--timerange", + "1515560100-1517287800", + "--export", + "signals", + "--cache", + "none", + ] + args = get_args(args) + start_backtesting(args) + + captured = capsys.readouterr() + assert "BACKTESTING REPORT" in captured.out + assert "EXIT REASON STATS" in captured.out + assert "LEFT OPEN TRADES REPORT" in captured.out + + base_args = [ + "backtesting-analysis", + "--config", + "config.json", + "--datadir", + str(testdatadir), + "--user-data-dir", + str(user_dir), + ] + + # test group 0 and indicator list + args = get_args( + base_args + + [ + "--analysis-groups", + "0", + "--indicator-list", + "close", + "rsi", + "profit_abs", + "--entry-only", + ] + ) + start_analysis_entries_exits(args) + captured = capsys.readouterr() + assert "LTC/BTC" in captured.out + assert "ETH/BTC" in captured.out + assert "enter_tag_long_a" in captured.out + assert "enter_tag_long_b" in captured.out + assert "exit_signal" in captured.out + assert "roi" in captured.out + assert "stop_loss" in captured.out + assert "trailing_stop_loss" in captured.out + assert "0.5" in captured.out + assert "-4" in captured.out + assert "-2" in captured.out + assert "-3.5" in captured.out + assert "50" in captured.out + assert "0" in captured.out + assert "0.016" in captured.out + assert "34.049" in captured.out + assert "0.104" in captured.out + assert "52.829" in captured.out + # assert indicator list + assert "close" in captured.out + assert "close (entry)" not in captured.out + assert "0.016" in captured.out + assert "rsi (entry)" not in captured.out + assert "rsi" in captured.out + assert "54.320" in captured.out + assert "close (exit)" not in captured.out + assert "rsi (exit)" not in captured.out + assert "52.829" in captured.out + assert "profit_abs" in captured.out From c9acb1466ca5580a958d3dd8ed49ae671d77bf71 Mon Sep 17 00:00:00 2001 From: Joe Schr <8218910+TheJoeSchr@users.noreply.github.com> Date: Tue, 10 Sep 2024 15:28:53 +0200 Subject: [PATCH 61/93] fix: orderflow data missing for plotting and other runmodes --- freqtrade/data/dataprovider.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index b4950f515b1..e40228511d4 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -520,7 +520,7 @@ def trades( return self._exchange.trades( (pair, timeframe or self._config["timeframe"], _candle_type), copy=copy ) - elif self.runmode in (RunMode.BACKTEST, RunMode.HYPEROPT): + else: data_handler = get_datahandler( self._config["datadir"], data_format=self._config["dataformat_trades"] ) @@ -529,9 +529,6 @@ def trades( ) return trades_df - else: - return DataFrame() - def market(self, pair: str) -> Optional[Dict[str, Any]]: """ Return market data for the pair From 439658fcf1222b18363296dcb022719775ad4a16 Mon Sep 17 00:00:00 2001 From: Joe Schr <8218910+TheJoeSchr@users.noreply.github.com> Date: Wed, 11 Sep 2024 16:50:27 +0200 Subject: [PATCH 62/93] fix: remove tests for orderflow data missing of other runmodes --- tests/data/test_dataprovider.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py index 8656da10b6b..220aafef0ea 100644 --- a/tests/data/test_dataprovider.py +++ b/tests/data/test_dataprovider.py @@ -90,13 +90,6 @@ def test_historic_trades(mocker, default_conf, trades_history_df): assert isinstance(data, DataFrame) assert len(data) == len(trades_history_df) - # Random other runmode - default_conf["runmode"] = RunMode.UTIL_EXCHANGE - dp = DataProvider(default_conf, None) - data = dp.trades("UNITTEST/BTC", "5m") - assert isinstance(data, DataFrame) - assert len(data) == 0 - def test_historic_ohlcv_dataformat(mocker, default_conf, ohlcv_history): hdf5loadmock = MagicMock(return_value=ohlcv_history) From d15921b3f2894c1e19045536178bc771c370ebd7 Mon Sep 17 00:00:00 2001 From: xmatthias <5024695+xmatthias@users.noreply.github.com> Date: Thu, 12 Sep 2024 03:12:54 +0000 Subject: [PATCH 63/93] chore: update pre-commit hooks --- .../exchange/binance_leverage_tiers.json | 2728 ++++++++++------- 1 file changed, 1638 insertions(+), 1090 deletions(-) diff --git a/freqtrade/exchange/binance_leverage_tiers.json b/freqtrade/exchange/binance_leverage_tiers.json index 859d87b9fd3..bcf497efe04 100644 --- a/freqtrade/exchange/binance_leverage_tiers.json +++ b/freqtrade/exchange/binance_leverage_tiers.json @@ -280,128 +280,144 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "75", + "notionalCap": "10000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 50000.0, + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 150000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { - "bracket": "2", + "bracket": "3", "initialLeverage": "25", - "notionalCap": "50000", - "notionalFloor": "5000", + "notionalCap": "150000", + "notionalFloor": "30000", "maintMarginRatio": "0.02", - "cum": "25.0" + "cum": "200.0" } }, { - "tier": 3.0, + "tier": 4.0, "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, + "minNotional": 150000.0, + "maxNotional": 300000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { - "bracket": "3", + "bracket": "4", "initialLeverage": "20", - "notionalCap": "100000", - "notionalFloor": "50000", + "notionalCap": "300000", + "notionalFloor": "150000", "maintMarginRatio": "0.025", - "cum": "275.0" + "cum": "950.0" } }, { - "tier": 4.0, + "tier": 5.0, "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 1000000.0, + "minNotional": 300000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "4", + "bracket": "5", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "100000", + "notionalCap": "1500000", + "notionalFloor": "300000", "maintMarginRatio": "0.05", - "cum": "2775.0" + "cum": "8450.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 1500000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "5", + "bracket": "6", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "3000000", + "notionalFloor": "1500000", "maintMarginRatio": "0.1", - "cum": "52775.0" + "cum": "83450.0" } }, { - "tier": 6.0, + "tier": 7.0, "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 2500000.0, + "minNotional": 3000000.0, + "maxNotional": 3750000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "6", + "bracket": "7", "initialLeverage": "4", - "notionalCap": "2500000", - "notionalFloor": "2000000", + "notionalCap": "3750000", + "notionalFloor": "3000000", "maintMarginRatio": "0.125", - "cum": "102775.0" + "cum": "158450.0" } }, { - "tier": 7.0, + "tier": 8.0, "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 3750000.0, + "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "7", + "bracket": "8", "initialLeverage": "2", - "notionalCap": "5000000", - "notionalFloor": "2500000", + "notionalCap": "7500000", + "notionalFloor": "3750000", "maintMarginRatio": "0.25", - "cum": "415275.0" + "cum": "627200.0" } }, { - "tier": 8.0, + "tier": 9.0, "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 7500000.0, + "maxNotional": 15000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "9", "initialLeverage": "1", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "15000000", + "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "1665275.0" + "cum": "2502200.0" } } ], @@ -1580,128 +1596,144 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "75", + "notionalCap": "10000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 10000.0, + "maxNotional": 40000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": "2", - "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", - "maintMarginRatio": "0.025", + "initialLeverage": "50", + "notionalCap": "40000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", "cum": "50.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.03, - "maxLeverage": 15.0, + "minNotional": 40000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "3", - "initialLeverage": "15", - "notionalCap": "100000", - "notionalFloor": "25000", - "maintMarginRatio": "0.03", - "cum": "175.0" + "initialLeverage": "25", + "notionalCap": "200000", + "notionalFloor": "40000", + "maintMarginRatio": "0.02", + "cum": "250.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 100000.0, + "minNotional": 200000.0, "maxNotional": 400000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "4", - "initialLeverage": "10", + "initialLeverage": "20", "notionalCap": "400000", - "notionalFloor": "100000", - "maintMarginRatio": "0.05", - "cum": "2175.0" + "notionalFloor": "200000", + "maintMarginRatio": "0.025", + "cum": "1250.0" } }, { "tier": 5.0, "currency": "USDT", "minNotional": 400000.0, - "maxNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "2000000", + "notionalFloor": "400000", + "maintMarginRatio": "0.05", + "cum": "11250.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "5", + "bracket": "6", "initialLeverage": "5", - "notionalCap": "1000000", - "notionalFloor": "400000", + "notionalCap": "4000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.1", - "cum": "22175.0" + "cum": "111250.0" } }, { - "tier": 6.0, + "tier": 7.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 4000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "6", + "bracket": "7", "initialLeverage": "4", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "5000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.125", - "cum": "47175.0" + "cum": "211250.0" } }, { - "tier": 7.0, + "tier": 8.0, "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 6000000.0, + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "7", + "bracket": "8", "initialLeverage": "2", - "notionalCap": "6000000", - "notionalFloor": "2000000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.25", - "cum": "297175.0" + "cum": "836250.0" } }, { - "tier": 8.0, + "tier": 9.0, "currency": "USDT", - "minNotional": 6000000.0, - "maxNotional": 10000000.0, + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "9", "initialLeverage": "1", - "notionalCap": "10000000", - "notionalFloor": "6000000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.5", - "cum": "1797175.0" + "cum": "3336250.0" } } ], @@ -1970,13 +2002,13 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.005, "maxLeverage": 75.0, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "5000", + "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.005", "cum": "0.0" @@ -1985,65 +2017,65 @@ { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.006, + "minNotional": 10000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.01, "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "10000", - "notionalFloor": "5000", - "maintMarginRatio": "0.006", - "cum": "5.0" + "notionalCap": "50000", + "notionalFloor": "10000", + "maintMarginRatio": "0.01", + "cum": "50.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.01, + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.015, "maxLeverage": 40.0, "info": { "bracket": "3", "initialLeverage": "40", - "notionalCap": "50000", - "notionalFloor": "10000", - "maintMarginRatio": "0.01", - "cum": "45.0" + "notionalCap": "100000", + "notionalFloor": "50000", + "maintMarginRatio": "0.015", + "cum": "300.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 250000.0, + "minNotional": 100000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "4", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "notionalCap": "500000", + "notionalFloor": "100000", "maintMarginRatio": "0.02", - "cum": "545.0" + "cum": "800.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 250000.0, + "minNotional": 500000.0, "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "20", "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.05", - "cum": "8045.0" + "notionalFloor": "500000", + "maintMarginRatio": "0.025", + "cum": "3300.0" } }, { @@ -2051,15 +2083,15 @@ "currency": "USDT", "minNotional": 1000000.0, "maxNotional": 5000000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "6", - "initialLeverage": "5", + "initialLeverage": "10", "notionalCap": "5000000", "notionalFloor": "1000000", - "maintMarginRatio": "0.1", - "cum": "58045.0" + "maintMarginRatio": "0.05", + "cum": "28300.0" } }, { @@ -2067,15 +2099,15 @@ "currency": "USDT", "minNotional": 5000000.0, "maxNotional": 10000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "7", - "initialLeverage": "4", + "initialLeverage": "5", "notionalCap": "10000000", "notionalFloor": "5000000", - "maintMarginRatio": "0.125", - "cum": "183045.0" + "maintMarginRatio": "0.1", + "cum": "278300.0" } }, { @@ -2083,15 +2115,15 @@ "currency": "USDT", "minNotional": 10000000.0, "maxNotional": 20000000.0, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "8", - "initialLeverage": "3", + "initialLeverage": "4", "notionalCap": "20000000", "notionalFloor": "10000000", - "maintMarginRatio": "0.15", - "cum": "433045.0" + "maintMarginRatio": "0.125", + "cum": "528300.0" } }, { @@ -2107,7 +2139,7 @@ "notionalCap": "30000000", "notionalFloor": "20000000", "maintMarginRatio": "0.25", - "cum": "2433045.0" + "cum": "3028300.0" } }, { @@ -2123,7 +2155,153 @@ "notionalCap": "50000000", "notionalFloor": "30000000", "maintMarginRatio": "0.5", - "cum": "9933045.0" + "cum": "10528300.0" + } + } + ], + "AERGO/USDT:USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, + "info": { + "bracket": "1", + "initialLeverage": "75", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 40000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "40000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "175.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 40000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "40000", + "maintMarginRatio": "0.05", + "cum": "1175.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "11175.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "21175.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83675.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "333675.0" } } ], @@ -3774,13 +3952,13 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50.0, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "75", + "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" @@ -3789,113 +3967,129 @@ { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 25000.0, + "minNotional": 10000.0, + "maxNotional": 60000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 25.0, + "maxLeverage": 50.0, "info": { "bracket": "2", - "initialLeverage": "25", - "notionalCap": "25000", - "notionalFloor": "5000", + "initialLeverage": "50", + "notionalCap": "60000", + "notionalFloor": "10000", "maintMarginRatio": "0.015", - "cum": "25.0" + "cum": "50.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, + "minNotional": 60000.0, "maxNotional": 900000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 20.0, + "maxLeverage": 25.0, "info": { "bracket": "3", - "initialLeverage": "20", + "initialLeverage": "25", "notionalCap": "900000", - "notionalFloor": "25000", + "notionalFloor": "60000", "maintMarginRatio": "0.02", - "cum": "150.0" + "cum": "350.0" } }, { "tier": 4.0, "currency": "USDT", "minNotional": 900000.0, - "maxNotional": 1800000.0, + "maxNotional": 1100000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "1100000", + "notionalFloor": "900000", + "maintMarginRatio": "0.025", + "cum": "4850.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 1100000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "4", + "bracket": "5", "initialLeverage": "10", - "notionalCap": "1800000", - "notionalFloor": "900000", + "notionalCap": "3000000", + "notionalFloor": "1100000", "maintMarginRatio": "0.05", - "cum": "27150.0" + "cum": "32350.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 1800000.0, - "maxNotional": 4800000.0, + "minNotional": 3000000.0, + "maxNotional": 6000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "5", + "bracket": "6", "initialLeverage": "5", - "notionalCap": "4800000", - "notionalFloor": "1800000", + "notionalCap": "6000000", + "notionalFloor": "3000000", "maintMarginRatio": "0.1", - "cum": "117150.0" + "cum": "182350.0" } }, { - "tier": 6.0, + "tier": 7.0, "currency": "USDT", - "minNotional": 4800000.0, - "maxNotional": 6000000.0, + "minNotional": 6000000.0, + "maxNotional": 7500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "6", + "bracket": "7", "initialLeverage": "4", - "notionalCap": "6000000", - "notionalFloor": "4800000", + "notionalCap": "7500000", + "notionalFloor": "6000000", "maintMarginRatio": "0.125", - "cum": "237150.0" + "cum": "332350.0" } }, { - "tier": 7.0, + "tier": 8.0, "currency": "USDT", - "minNotional": 6000000.0, + "minNotional": 7500000.0, "maxNotional": 18000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "7", + "bracket": "8", "initialLeverage": "2", "notionalCap": "18000000", - "notionalFloor": "6000000", + "notionalFloor": "7500000", "maintMarginRatio": "0.25", - "cum": "987150.0" + "cum": "1269850.0" } }, { - "tier": 8.0, + "tier": 9.0, "currency": "USDT", "minNotional": 18000000.0, "maxNotional": 30000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "9", "initialLeverage": "1", "notionalCap": "30000000", "notionalFloor": "18000000", "maintMarginRatio": "0.5", - "cum": "5487150.0" + "cum": "5769850.0" } } ], @@ -8727,6 +8921,152 @@ } } ], + "BSW/USDT:USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, + "info": { + "bracket": "1", + "initialLeverage": "75", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 40000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "40000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "175.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 40000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "40000", + "maintMarginRatio": "0.05", + "cum": "1175.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "11175.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "21175.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83675.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "333675.0" + } + } + ], "BTC/USDC:USDC": [ { "tier": 1.0, @@ -13710,128 +14050,144 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "75", + "notionalCap": "10000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 50000.0, + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 150000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { - "bracket": "2", + "bracket": "3", "initialLeverage": "25", - "notionalCap": "50000", - "notionalFloor": "5000", + "notionalCap": "150000", + "notionalFloor": "30000", "maintMarginRatio": "0.02", - "cum": "25.0" + "cum": "200.0" } }, { - "tier": 3.0, + "tier": 4.0, "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 150000.0, + "minNotional": 150000.0, + "maxNotional": 300000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { - "bracket": "3", + "bracket": "4", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "notionalCap": "300000", + "notionalFloor": "150000", "maintMarginRatio": "0.025", - "cum": "275.0" + "cum": "950.0" } }, { - "tier": 4.0, + "tier": 5.0, "currency": "USDT", - "minNotional": 150000.0, + "minNotional": 300000.0, "maxNotional": 1500000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "4", + "bracket": "5", "initialLeverage": "10", "notionalCap": "1500000", - "notionalFloor": "150000", + "notionalFloor": "300000", "maintMarginRatio": "0.05", - "cum": "4025.0" + "cum": "8450.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", "minNotional": 1500000.0, "maxNotional": 3000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "5", + "bracket": "6", "initialLeverage": "5", "notionalCap": "3000000", "notionalFloor": "1500000", "maintMarginRatio": "0.1", - "cum": "79025.0" + "cum": "83450.0" } }, { - "tier": 6.0, + "tier": 7.0, "currency": "USDT", "minNotional": 3000000.0, "maxNotional": 3750000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "6", + "bracket": "7", "initialLeverage": "4", "notionalCap": "3750000", "notionalFloor": "3000000", "maintMarginRatio": "0.125", - "cum": "154025.0" + "cum": "158450.0" } }, { - "tier": 7.0, + "tier": 8.0, "currency": "USDT", "minNotional": 3750000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "7", + "bracket": "8", "initialLeverage": "2", "notionalCap": "7500000", "notionalFloor": "3750000", "maintMarginRatio": "0.25", - "cum": "622775.0" + "cum": "627200.0" } }, { - "tier": 8.0, + "tier": 9.0, "currency": "USDT", "minNotional": 7500000.0, "maxNotional": 15000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "9", "initialLeverage": "1", "notionalCap": "15000000", "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "2497775.0" + "cum": "2502200.0" } } ], @@ -15560,128 +15916,144 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 20.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "20", - "notionalCap": "5000", + "initialLeverage": "75", + "notionalCap": "10000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 15.0, + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": "2", - "initialLeverage": "15", - "notionalCap": "25000", - "notionalFloor": "5000", - "maintMarginRatio": "0.02", - "cum": "25.0" + "initialLeverage": "50", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 80000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 10.0, + "minNotional": 30000.0, + "maxNotional": 150000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "3", - "initialLeverage": "10", - "notionalCap": "80000", - "notionalFloor": "25000", - "maintMarginRatio": "0.025", - "cum": "150.0" + "initialLeverage": "25", + "notionalCap": "150000", + "notionalFloor": "30000", + "maintMarginRatio": "0.02", + "cum": "200.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 80000.0, - "maxNotional": 800000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 8.0, + "minNotional": 150000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "4", - "initialLeverage": "8", - "notionalCap": "800000", - "notionalFloor": "80000", - "maintMarginRatio": "0.05", - "cum": "2150.0" + "initialLeverage": "20", + "notionalCap": "300000", + "notionalFloor": "150000", + "maintMarginRatio": "0.025", + "cum": "950.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 800000.0, - "maxNotional": 1600000.0, + "minNotional": 300000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "1500000", + "notionalFloor": "300000", + "maintMarginRatio": "0.05", + "cum": "8450.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "5", + "bracket": "6", "initialLeverage": "5", - "notionalCap": "1600000", - "notionalFloor": "800000", + "notionalCap": "3000000", + "notionalFloor": "1500000", "maintMarginRatio": "0.1", - "cum": "42150.0" + "cum": "83450.0" } }, { - "tier": 6.0, + "tier": 7.0, "currency": "USDT", - "minNotional": 1600000.0, - "maxNotional": 2000000.0, + "minNotional": 3000000.0, + "maxNotional": 3750000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "6", + "bracket": "7", "initialLeverage": "4", - "notionalCap": "2000000", - "notionalFloor": "1600000", + "notionalCap": "3750000", + "notionalFloor": "3000000", "maintMarginRatio": "0.125", - "cum": "82150.0" + "cum": "158450.0" } }, { - "tier": 7.0, + "tier": 8.0, "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 4000000.0, + "minNotional": 3750000.0, + "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "7", + "bracket": "8", "initialLeverage": "2", - "notionalCap": "4000000", - "notionalFloor": "2000000", + "notionalCap": "7500000", + "notionalFloor": "3750000", "maintMarginRatio": "0.25", - "cum": "332150.0" + "cum": "627200.0" } }, { - "tier": 8.0, + "tier": 9.0, "currency": "USDT", - "minNotional": 4000000.0, - "maxNotional": 5000000.0, + "minNotional": 7500000.0, + "maxNotional": 15000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "9", "initialLeverage": "1", - "notionalCap": "5000000", - "notionalFloor": "4000000", + "notionalCap": "15000000", + "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "1332150.0" + "cum": "2502200.0" } } ], @@ -16351,20 +16723,20 @@ } } ], - "FRONT/USDT:USDT": [ + "FTM/USDT:USDT": [ { "tier": 1.0, "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 21.0, + "maintenanceMarginRate": 0.006, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "21", + "initialLeverage": "75", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.006", "cum": "0.0" } }, @@ -16372,226 +16744,144 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, "info": { "bracket": "2", - "initialLeverage": "20", - "notionalCap": "25000", + "initialLeverage": "50", + "notionalCap": "50000", "notionalFloor": "5000", - "maintMarginRatio": "0.025", - "cum": "50.0" + "maintMarginRatio": "0.01", + "cum": "20.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 200000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 50000.0, + "maxNotional": 80000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 40.0, "info": { "bracket": "3", - "initialLeverage": "10", - "notionalCap": "200000", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "675.0" + "initialLeverage": "40", + "notionalCap": "80000", + "notionalFloor": "50000", + "maintMarginRatio": "0.015", + "cum": "270.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, - "info": { - "bracket": "4", - "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "200000", - "maintMarginRatio": "0.1", - "cum": "10675.0" - } - }, - { - "tier": 5.0, - "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": "5", - "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", - "maintMarginRatio": "0.125", - "cum": "23175.0" - } - }, - { - "tier": 6.0, - "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 3000000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": "6", - "initialLeverage": "2", - "notionalCap": "3000000", - "notionalFloor": "1000000", - "maintMarginRatio": "0.25", - "cum": "148175.0" - } - }, - { - "tier": 7.0, - "currency": "USDT", - "minNotional": 3000000.0, - "maxNotional": 3500000.0, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1.0, - "info": { - "bracket": "7", - "initialLeverage": "1", - "notionalCap": "3500000", - "notionalFloor": "3000000", - "maintMarginRatio": "0.5", - "cum": "898175.0" - } - } - ], - "FTM/USDT:USDT": [ - { - "tier": 1.0, - "currency": "USDT", - "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.006, - "maxLeverage": 50.0, - "info": { - "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", - "maintMarginRatio": "0.006", - "cum": "0.0" - } - }, - { - "tier": 2.0, - "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.01, + "minNotional": 80000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { - "bracket": "2", + "bracket": "4", "initialLeverage": "25", - "notionalCap": "50000", - "notionalFloor": "5000", - "maintMarginRatio": "0.01", - "cum": "20.0" + "notionalCap": "300000", + "notionalFloor": "80000", + "maintMarginRatio": "0.02", + "cum": "670.0" } }, { - "tier": 3.0, + "tier": 5.0, "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 400000.0, + "minNotional": 300000.0, + "maxNotional": 600000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { - "bracket": "3", + "bracket": "5", "initialLeverage": "20", - "notionalCap": "400000", - "notionalFloor": "50000", + "notionalCap": "600000", + "notionalFloor": "300000", "maintMarginRatio": "0.025", - "cum": "770.0" + "cum": "2170.0" } }, { - "tier": 4.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 400000.0, - "maxNotional": 800000.0, + "minNotional": 600000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "4", + "bracket": "6", "initialLeverage": "10", - "notionalCap": "800000", - "notionalFloor": "400000", + "notionalCap": "3000000", + "notionalFloor": "600000", "maintMarginRatio": "0.05", - "cum": "10770.0" + "cum": "17170.0" } }, { - "tier": 5.0, + "tier": 7.0, "currency": "USDT", - "minNotional": 800000.0, - "maxNotional": 2000000.0, + "minNotional": 3000000.0, + "maxNotional": 6000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "5", + "bracket": "7", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "800000", + "notionalCap": "6000000", + "notionalFloor": "3000000", "maintMarginRatio": "0.1", - "cum": "50770.0" + "cum": "167170.0" } }, { - "tier": 6.0, + "tier": 8.0, "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 5000000.0, + "minNotional": 6000000.0, + "maxNotional": 7500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "6", + "bracket": "8", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "notionalCap": "7500000", + "notionalFloor": "6000000", "maintMarginRatio": "0.125", - "cum": "100770.0" + "cum": "317170.0" } }, { - "tier": 7.0, + "tier": 9.0, "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 12000000.0, + "minNotional": 7500000.0, + "maxNotional": 15000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "7", + "bracket": "9", "initialLeverage": "2", - "notionalCap": "12000000", - "notionalFloor": "5000000", + "notionalCap": "15000000", + "notionalFloor": "7500000", "maintMarginRatio": "0.25", - "cum": "725770.0" + "cum": "1254670.0" } }, { - "tier": 8.0, + "tier": 10.0, "currency": "USDT", - "minNotional": 12000000.0, - "maxNotional": 20000000.0, + "minNotional": 15000000.0, + "maxNotional": 30000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "10", "initialLeverage": "1", - "notionalCap": "20000000", - "notionalFloor": "12000000", + "notionalCap": "30000000", + "notionalFloor": "15000000", "maintMarginRatio": "0.5", - "cum": "3725770.0" + "cum": "5004670.0" } } ], @@ -16910,13 +17200,13 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.006, - "maxLeverage": 50.0, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "75", + "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.006", "cum": "0.0" @@ -16925,113 +17215,145 @@ { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, + "minNotional": 10000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 25.0, + "maxLeverage": 50.0, "info": { "bracket": "2", - "initialLeverage": "25", + "initialLeverage": "50", "notionalCap": "50000", - "notionalFloor": "5000", + "notionalFloor": "10000", "maintMarginRatio": "0.01", - "cum": "20.0" + "cum": "40.0" } }, { "tier": 3.0, "currency": "USDT", "minNotional": 50000.0, + "maxNotional": 80000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 40.0, + "info": { + "bracket": "3", + "initialLeverage": "40", + "notionalCap": "80000", + "notionalFloor": "50000", + "maintMarginRatio": "0.015", + "cum": "290.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 80000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "4", + "initialLeverage": "25", + "notionalCap": "200000", + "notionalFloor": "80000", + "maintMarginRatio": "0.02", + "cum": "690.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 200000.0, "maxNotional": 900000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { - "bracket": "3", + "bracket": "5", "initialLeverage": "20", "notionalCap": "900000", - "notionalFloor": "50000", + "notionalFloor": "200000", "maintMarginRatio": "0.025", - "cum": "770.0" + "cum": "1690.0" } }, { - "tier": 4.0, + "tier": 6.0, "currency": "USDT", "minNotional": 900000.0, - "maxNotional": 1800000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "4", + "bracket": "6", "initialLeverage": "10", - "notionalCap": "1800000", + "notionalCap": "2000000", "notionalFloor": "900000", "maintMarginRatio": "0.05", - "cum": "23270.0" + "cum": "24190.0" } }, { - "tier": 5.0, + "tier": 7.0, "currency": "USDT", - "minNotional": 1800000.0, - "maxNotional": 4800000.0, + "minNotional": 2000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "5", + "bracket": "7", "initialLeverage": "5", - "notionalCap": "4800000", - "notionalFloor": "1800000", + "notionalCap": "5000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.1", - "cum": "113270.0" + "cum": "124190.0" } }, { - "tier": 6.0, + "tier": 8.0, "currency": "USDT", - "minNotional": 4800000.0, + "minNotional": 5000000.0, "maxNotional": 6000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "6", + "bracket": "8", "initialLeverage": "4", "notionalCap": "6000000", - "notionalFloor": "4800000", + "notionalFloor": "5000000", "maintMarginRatio": "0.125", - "cum": "233270.0" + "cum": "249190.0" } }, { - "tier": 7.0, + "tier": 9.0, "currency": "USDT", "minNotional": 6000000.0, "maxNotional": 18000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "7", + "bracket": "9", "initialLeverage": "2", "notionalCap": "18000000", "notionalFloor": "6000000", "maintMarginRatio": "0.25", - "cum": "983270.0" + "cum": "999190.0" } }, { - "tier": 8.0, + "tier": 10.0, "currency": "USDT", "minNotional": 18000000.0, "maxNotional": 30000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "10", "initialLeverage": "1", "notionalCap": "30000000", "notionalFloor": "18000000", "maintMarginRatio": "0.5", - "cum": "5483270.0" + "cum": "5499190.0" } } ], @@ -22686,13 +23008,13 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.005, "maxLeverage": 75.0, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "5000", + "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.005", "cum": "0.0" @@ -22701,39 +23023,39 @@ { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.006, + "minNotional": 10000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.01, "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "10000", - "notionalFloor": "5000", - "maintMarginRatio": "0.006", - "cum": "5.0" + "notionalCap": "50000", + "notionalFloor": "10000", + "maintMarginRatio": "0.01", + "cum": "50.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.01, + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.015, "maxLeverage": 40.0, "info": { "bracket": "3", "initialLeverage": "40", - "notionalCap": "50000", - "notionalFloor": "10000", - "maintMarginRatio": "0.01", - "cum": "45.0" + "notionalCap": "100000", + "notionalFloor": "50000", + "maintMarginRatio": "0.015", + "cum": "300.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 50000.0, + "minNotional": 100000.0, "maxNotional": 750000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, @@ -22741,73 +23063,73 @@ "bracket": "4", "initialLeverage": "25", "notionalCap": "750000", - "notionalFloor": "50000", + "notionalFloor": "100000", "maintMarginRatio": "0.02", - "cum": "545.0" + "cum": "800.0" } }, { "tier": 5.0, "currency": "USDT", "minNotional": 750000.0, - "maxNotional": 3000000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "5", + "initialLeverage": "20", + "notionalCap": "1000000", + "notionalFloor": "750000", + "maintMarginRatio": "0.025", + "cum": "4550.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "5", + "bracket": "6", "initialLeverage": "10", - "notionalCap": "3000000", - "notionalFloor": "750000", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.05", - "cum": "23045.0" + "cum": "29550.0" } }, { - "tier": 6.0, + "tier": 7.0, "currency": "USDT", - "minNotional": 3000000.0, + "minNotional": 5000000.0, "maxNotional": 10000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "6", + "bracket": "7", "initialLeverage": "5", "notionalCap": "10000000", - "notionalFloor": "3000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.1", - "cum": "173045.0" + "cum": "279550.0" } }, { - "tier": 7.0, + "tier": 8.0, "currency": "USDT", "minNotional": 10000000.0, - "maxNotional": 12000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "7", + "bracket": "8", "initialLeverage": "4", - "notionalCap": "12000000", + "notionalCap": "20000000", "notionalFloor": "10000000", "maintMarginRatio": "0.125", - "cum": "423045.0" - } - }, - { - "tier": 8.0, - "currency": "USDT", - "minNotional": 12000000.0, - "maxNotional": 20000000.0, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3.0, - "info": { - "bracket": "8", - "initialLeverage": "3", - "notionalCap": "20000000", - "notionalFloor": "12000000", - "maintMarginRatio": "0.15", - "cum": "723045.0" + "cum": "529550.0" } }, { @@ -22823,7 +23145,7 @@ "notionalCap": "30000000", "notionalFloor": "20000000", "maintMarginRatio": "0.25", - "cum": "2723045.0" + "cum": "3029550.0" } }, { @@ -22839,7 +23161,7 @@ "notionalCap": "50000000", "notionalFloor": "30000000", "maintMarginRatio": "0.5", - "cum": "10223045.0" + "cum": "10529550.0" } } ], @@ -23445,298 +23767,6 @@ } } ], - "MATIC/USDC:USDC": [ - { - "tier": 1.0, - "currency": "USDC", - "minNotional": 0.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.006, - "maxLeverage": 51.0, - "info": { - "bracket": "1", - "initialLeverage": "51", - "notionalCap": "10000", - "notionalFloor": "0", - "maintMarginRatio": "0.006", - "cum": "0.0" - } - }, - { - "tier": 2.0, - "currency": "USDC", - "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.007, - "maxLeverage": 50.0, - "info": { - "bracket": "2", - "initialLeverage": "50", - "notionalCap": "25000", - "notionalFloor": "10000", - "maintMarginRatio": "0.007", - "cum": "10.0" - } - }, - { - "tier": 3.0, - "currency": "USDC", - "minNotional": 25000.0, - "maxNotional": 600000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25.0, - "info": { - "bracket": "3", - "initialLeverage": "25", - "notionalCap": "600000", - "notionalFloor": "25000", - "maintMarginRatio": "0.01", - "cum": "85.0" - } - }, - { - "tier": 4.0, - "currency": "USDC", - "minNotional": 600000.0, - "maxNotional": 900000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, - "info": { - "bracket": "4", - "initialLeverage": "20", - "notionalCap": "900000", - "notionalFloor": "600000", - "maintMarginRatio": "0.025", - "cum": "9085.0" - } - }, - { - "tier": 5.0, - "currency": "USDC", - "minNotional": 900000.0, - "maxNotional": 1800000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, - "info": { - "bracket": "5", - "initialLeverage": "10", - "notionalCap": "1800000", - "notionalFloor": "900000", - "maintMarginRatio": "0.05", - "cum": "31585.0" - } - }, - { - "tier": 6.0, - "currency": "USDC", - "minNotional": 1800000.0, - "maxNotional": 4800000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, - "info": { - "bracket": "6", - "initialLeverage": "5", - "notionalCap": "4800000", - "notionalFloor": "1800000", - "maintMarginRatio": "0.1", - "cum": "121585.0" - } - }, - { - "tier": 7.0, - "currency": "USDC", - "minNotional": 4800000.0, - "maxNotional": 6000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": "7", - "initialLeverage": "4", - "notionalCap": "6000000", - "notionalFloor": "4800000", - "maintMarginRatio": "0.125", - "cum": "241585.0" - } - }, - { - "tier": 8.0, - "currency": "USDC", - "minNotional": 6000000.0, - "maxNotional": 18000000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": "8", - "initialLeverage": "2", - "notionalCap": "18000000", - "notionalFloor": "6000000", - "maintMarginRatio": "0.25", - "cum": "991585.0" - } - }, - { - "tier": 9.0, - "currency": "USDC", - "minNotional": 18000000.0, - "maxNotional": 19000000.0, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1.0, - "info": { - "bracket": "9", - "initialLeverage": "1", - "notionalCap": "19000000", - "notionalFloor": "18000000", - "maintMarginRatio": "0.5", - "cum": "5491585.0" - } - } - ], - "MATIC/USDT:USDT": [ - { - "tier": 1.0, - "currency": "USDT", - "minNotional": 0.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.006, - "maxLeverage": 51.0, - "info": { - "bracket": "1", - "initialLeverage": "51", - "notionalCap": "10000", - "notionalFloor": "0", - "maintMarginRatio": "0.006", - "cum": "0.0" - } - }, - { - "tier": 2.0, - "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.007, - "maxLeverage": 50.0, - "info": { - "bracket": "2", - "initialLeverage": "50", - "notionalCap": "25000", - "notionalFloor": "10000", - "maintMarginRatio": "0.007", - "cum": "10.0" - } - }, - { - "tier": 3.0, - "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 600000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25.0, - "info": { - "bracket": "3", - "initialLeverage": "25", - "notionalCap": "600000", - "notionalFloor": "25000", - "maintMarginRatio": "0.01", - "cum": "85.0" - } - }, - { - "tier": 4.0, - "currency": "USDT", - "minNotional": 600000.0, - "maxNotional": 900000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, - "info": { - "bracket": "4", - "initialLeverage": "20", - "notionalCap": "900000", - "notionalFloor": "600000", - "maintMarginRatio": "0.025", - "cum": "9085.0" - } - }, - { - "tier": 5.0, - "currency": "USDT", - "minNotional": 900000.0, - "maxNotional": 1800000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, - "info": { - "bracket": "5", - "initialLeverage": "10", - "notionalCap": "1800000", - "notionalFloor": "900000", - "maintMarginRatio": "0.05", - "cum": "31585.0" - } - }, - { - "tier": 6.0, - "currency": "USDT", - "minNotional": 1800000.0, - "maxNotional": 4800000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, - "info": { - "bracket": "6", - "initialLeverage": "5", - "notionalCap": "4800000", - "notionalFloor": "1800000", - "maintMarginRatio": "0.1", - "cum": "121585.0" - } - }, - { - "tier": 7.0, - "currency": "USDT", - "minNotional": 4800000.0, - "maxNotional": 6000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": "7", - "initialLeverage": "4", - "notionalCap": "6000000", - "notionalFloor": "4800000", - "maintMarginRatio": "0.125", - "cum": "241585.0" - } - }, - { - "tier": 8.0, - "currency": "USDT", - "minNotional": 6000000.0, - "maxNotional": 18000000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": "8", - "initialLeverage": "2", - "notionalCap": "18000000", - "notionalFloor": "6000000", - "maintMarginRatio": "0.25", - "cum": "991585.0" - } - }, - { - "tier": 9.0, - "currency": "USDT", - "minNotional": 18000000.0, - "maxNotional": 19000000.0, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1.0, - "info": { - "bracket": "9", - "initialLeverage": "1", - "notionalCap": "19000000", - "notionalFloor": "18000000", - "maintMarginRatio": "0.5", - "cum": "5491585.0" - } - } - ], "MAV/USDT:USDT": [ { "tier": 1.0, @@ -25461,6 +25491,152 @@ } } ], + "NEIROETH/USDT:USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, + "info": { + "bracket": "1", + "initialLeverage": "75", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 40000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "40000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "175.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 40000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "40000", + "maintMarginRatio": "0.05", + "cum": "1175.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "11175.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "21175.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83675.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "333675.0" + } + } + ], "NEO/USDC:USDC": [ { "tier": 1.0, @@ -29657,6 +29833,152 @@ } } ], + "QUICK/USDT:USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, + "info": { + "bracket": "1", + "initialLeverage": "75", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 40000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "40000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "175.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 40000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "40000", + "maintMarginRatio": "0.05", + "cum": "1175.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "11175.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "21175.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83675.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "333675.0" + } + } + ], "RAD/USDT:USDT": [ { "tier": 1.0, @@ -30020,112 +30342,144 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "75", + "notionalCap": "10000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 50000.0, + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "100000", + "notionalFloor": "20000", + "maintMarginRatio": "0.02", + "cum": "150.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 200000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { - "bracket": "2", + "bracket": "4", "initialLeverage": "20", - "notionalCap": "50000", - "notionalFloor": "5000", + "notionalCap": "200000", + "notionalFloor": "100000", "maintMarginRatio": "0.025", - "cum": "50.0" + "cum": "650.0" } }, { - "tier": 3.0, + "tier": 5.0, "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 600000.0, + "minNotional": 200000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "3", + "bracket": "5", "initialLeverage": "10", - "notionalCap": "600000", - "notionalFloor": "50000", + "notionalCap": "1000000", + "notionalFloor": "200000", "maintMarginRatio": "0.05", - "cum": "1300.0" + "cum": "5650.0" } }, { - "tier": 4.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 600000.0, - "maxNotional": 1600000.0, + "minNotional": 1000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "4", + "bracket": "6", "initialLeverage": "5", - "notionalCap": "1600000", - "notionalFloor": "600000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.1", - "cum": "31300.0" + "cum": "55650.0" } }, { - "tier": 5.0, + "tier": 7.0, "currency": "USDT", - "minNotional": 1600000.0, - "maxNotional": 2000000.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "5", + "bracket": "7", "initialLeverage": "4", - "notionalCap": "2000000", - "notionalFloor": "1600000", + "notionalCap": "2500000", + "notionalFloor": "2000000", "maintMarginRatio": "0.125", - "cum": "71300.0" + "cum": "105650.0" } }, { - "tier": 6.0, + "tier": 8.0, "currency": "USDT", - "minNotional": 2000000.0, + "minNotional": 2500000.0, "maxNotional": 6000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "6", + "bracket": "8", "initialLeverage": "2", "notionalCap": "6000000", - "notionalFloor": "2000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.25", - "cum": "321300.0" + "cum": "418150.0" } }, { - "tier": 7.0, + "tier": 9.0, "currency": "USDT", "minNotional": 6000000.0, "maxNotional": 10000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "7", + "bracket": "9", "initialLeverage": "1", "notionalCap": "10000000", "notionalFloor": "6000000", "maintMarginRatio": "0.5", - "cum": "1821300.0" + "cum": "1918150.0" } } ], @@ -31089,6 +31443,152 @@ } } ], + "RPL/USDT:USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, + "info": { + "bracket": "1", + "initialLeverage": "75", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 40000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "40000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "175.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 40000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "40000", + "maintMarginRatio": "0.05", + "cum": "1175.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "11175.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "21175.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83675.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "333675.0" + } + } + ], "RSR/USDT:USDT": [ { "tier": 1.0, @@ -34250,13 +34750,13 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 10000.0, + "maxNotional": 16000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "10000", + "notionalCap": "16000", "notionalFloor": "5000", "maintMarginRatio": "0.015", "cum": "25.0" @@ -34265,113 +34765,113 @@ { "tier": 3.0, "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 20000.0, + "minNotional": 16000.0, + "maxNotional": 80000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "20000", - "notionalFloor": "10000", + "notionalCap": "80000", + "notionalFloor": "16000", "maintMarginRatio": "0.02", - "cum": "75.0" + "cum": "105.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 40000.0, + "minNotional": 80000.0, + "maxNotional": 160000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "4", "initialLeverage": "20", - "notionalCap": "40000", - "notionalFloor": "20000", + "notionalCap": "160000", + "notionalFloor": "80000", "maintMarginRatio": "0.025", - "cum": "175.0" + "cum": "505.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 40000.0, - "maxNotional": 200000.0, + "minNotional": 160000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "200000", - "notionalFloor": "40000", + "notionalCap": "800000", + "notionalFloor": "160000", "maintMarginRatio": "0.05", - "cum": "1175.0" + "cum": "4505.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 400000.0, + "minNotional": 800000.0, + "maxNotional": 1600000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "400000", - "notionalFloor": "200000", + "notionalCap": "1600000", + "notionalFloor": "800000", "maintMarginRatio": "0.1", - "cum": "11175.0" + "cum": "44505.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 400000.0, - "maxNotional": 500000.0, + "minNotional": 1600000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "500000", - "notionalFloor": "400000", + "notionalCap": "2000000", + "notionalFloor": "1600000", "maintMarginRatio": "0.125", - "cum": "21175.0" + "cum": "84505.0" } }, { "tier": 8.0, "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, + "minNotional": 2000000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "500000", + "notionalCap": "4000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.25", - "cum": "83675.0" + "cum": "334505.0" } }, { "tier": 9.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 4000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "8000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.5", - "cum": "333675.0" + "cum": "1334505.0" } } ], @@ -35113,14 +35613,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "75", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.01", "cum": "0.0" } }, @@ -35128,112 +35628,128 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 16000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": "2", - "initialLeverage": "25", - "notionalCap": "20000", + "initialLeverage": "50", + "notionalCap": "16000", "notionalFloor": "5000", - "maintMarginRatio": "0.02", + "maintMarginRatio": "0.015", "cum": "25.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 30000.0, + "minNotional": 16000.0, + "maxNotional": 80000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "80000", + "notionalFloor": "16000", + "maintMarginRatio": "0.02", + "cum": "105.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 80000.0, + "maxNotional": 160000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { - "bracket": "3", + "bracket": "4", "initialLeverage": "20", - "notionalCap": "30000", - "notionalFloor": "20000", + "notionalCap": "160000", + "notionalFloor": "80000", "maintMarginRatio": "0.025", - "cum": "125.0" + "cum": "505.0" } }, { - "tier": 4.0, + "tier": 5.0, "currency": "USDT", - "minNotional": 30000.0, - "maxNotional": 300000.0, + "minNotional": 160000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "4", + "bracket": "5", "initialLeverage": "10", - "notionalCap": "300000", - "notionalFloor": "30000", + "notionalCap": "800000", + "notionalFloor": "160000", "maintMarginRatio": "0.05", - "cum": "875.0" + "cum": "4505.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 300000.0, - "maxNotional": 600000.0, + "minNotional": 800000.0, + "maxNotional": 1600000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "5", + "bracket": "6", "initialLeverage": "5", - "notionalCap": "600000", - "notionalFloor": "300000", + "notionalCap": "1600000", + "notionalFloor": "800000", "maintMarginRatio": "0.1", - "cum": "15875.0" + "cum": "44505.0" } }, { - "tier": 6.0, + "tier": 7.0, "currency": "USDT", - "minNotional": 600000.0, - "maxNotional": 750000.0, + "minNotional": 1600000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "6", + "bracket": "7", "initialLeverage": "4", - "notionalCap": "750000", - "notionalFloor": "600000", + "notionalCap": "2000000", + "notionalFloor": "1600000", "maintMarginRatio": "0.125", - "cum": "30875.0" + "cum": "84505.0" } }, { - "tier": 7.0, + "tier": 8.0, "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 1500000.0, + "minNotional": 2000000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "7", + "bracket": "8", "initialLeverage": "2", - "notionalCap": "1500000", - "notionalFloor": "750000", + "notionalCap": "4000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.25", - "cum": "124625.0" + "cum": "334505.0" } }, { - "tier": 8.0, + "tier": 9.0, "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 3000000.0, + "minNotional": 4000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "9", "initialLeverage": "1", - "notionalCap": "3000000", - "notionalFloor": "1500000", + "notionalCap": "8000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.5", - "cum": "499625.0" + "cum": "1334505.0" } } ], @@ -36412,112 +36928,128 @@ "tier": 3.0, "currency": "USDT", "minNotional": 90000.0, - "maxNotional": 645000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 20.0, + "maxNotional": 120000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 40.0, "info": { "bracket": "3", - "initialLeverage": "20", - "notionalCap": "645000", + "initialLeverage": "40", + "notionalCap": "120000", "notionalFloor": "90000", - "maintMarginRatio": "0.02", - "cum": "935.0" + "maintMarginRatio": "0.015", + "cum": "485.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 645000.0, - "maxNotional": 1200000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 120000.0, + "maxNotional": 650000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "4", - "initialLeverage": "10", - "notionalCap": "1200000", - "notionalFloor": "645000", - "maintMarginRatio": "0.05", - "cum": "20285.0" + "initialLeverage": "25", + "notionalCap": "650000", + "notionalFloor": "120000", + "maintMarginRatio": "0.02", + "cum": "1085.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 1200000.0, - "maxNotional": 3000000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 650000.0, + "maxNotional": 800000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "5", - "initialLeverage": "5", - "notionalCap": "3000000", - "notionalFloor": "1200000", - "maintMarginRatio": "0.1", - "cum": "80285.0" + "initialLeverage": "20", + "notionalCap": "800000", + "notionalFloor": "650000", + "maintMarginRatio": "0.025", + "cum": "4335.0" } }, { "tier": 6.0, "currency": "USDT", + "minNotional": 800000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "6", + "initialLeverage": "10", + "notionalCap": "3000000", + "notionalFloor": "800000", + "maintMarginRatio": "0.05", + "cum": "24335.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", "minNotional": 3000000.0, "maxNotional": 6000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { - "bracket": "6", - "initialLeverage": "4", + "bracket": "7", + "initialLeverage": "5", "notionalCap": "6000000", "notionalFloor": "3000000", - "maintMarginRatio": "0.125", - "cum": "155285.0" + "maintMarginRatio": "0.1", + "cum": "174335.0" } }, { - "tier": 7.0, + "tier": 8.0, "currency": "USDT", "minNotional": 6000000.0, "maxNotional": 12000000.0, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { - "bracket": "7", - "initialLeverage": "3", + "bracket": "8", + "initialLeverage": "4", "notionalCap": "12000000", "notionalFloor": "6000000", - "maintMarginRatio": "0.15", - "cum": "305285.0" + "maintMarginRatio": "0.125", + "cum": "324335.0" } }, { - "tier": 8.0, + "tier": 9.0, "currency": "USDT", "minNotional": 12000000.0, "maxNotional": 20000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "8", + "bracket": "9", "initialLeverage": "2", "notionalCap": "20000000", "notionalFloor": "12000000", "maintMarginRatio": "0.25", - "cum": "1505285.0" + "cum": "1824335.0" } }, { - "tier": 9.0, + "tier": 10.0, "currency": "USDT", "minNotional": 20000000.0, "maxNotional": 30000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "9", + "bracket": "10", "initialLeverage": "1", "notionalCap": "30000000", "notionalFloor": "20000000", "maintMarginRatio": "0.5", - "cum": "6505285.0" + "cum": "6824335.0" } } ], @@ -37632,13 +38164,13 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 10000.0, + "maxNotional": 16000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "10000", + "notionalCap": "16000", "notionalFloor": "5000", "maintMarginRatio": "0.015", "cum": "25.0" @@ -37647,113 +38179,113 @@ { "tier": 3.0, "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 20000.0, + "minNotional": 16000.0, + "maxNotional": 80000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "20000", - "notionalFloor": "10000", + "notionalCap": "80000", + "notionalFloor": "16000", "maintMarginRatio": "0.02", - "cum": "75.0" + "cum": "105.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 40000.0, + "minNotional": 80000.0, + "maxNotional": 160000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "4", "initialLeverage": "20", - "notionalCap": "40000", - "notionalFloor": "20000", + "notionalCap": "160000", + "notionalFloor": "80000", "maintMarginRatio": "0.025", - "cum": "175.0" + "cum": "505.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 40000.0, - "maxNotional": 200000.0, + "minNotional": 160000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "200000", - "notionalFloor": "40000", + "notionalCap": "800000", + "notionalFloor": "160000", "maintMarginRatio": "0.05", - "cum": "1175.0" + "cum": "4505.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 400000.0, + "minNotional": 800000.0, + "maxNotional": 1600000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "400000", - "notionalFloor": "200000", + "notionalCap": "1600000", + "notionalFloor": "800000", "maintMarginRatio": "0.1", - "cum": "11175.0" + "cum": "44505.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 400000.0, - "maxNotional": 500000.0, + "minNotional": 1600000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "500000", - "notionalFloor": "400000", + "notionalCap": "2000000", + "notionalFloor": "1600000", "maintMarginRatio": "0.125", - "cum": "21175.0" + "cum": "84505.0" } }, { "tier": 8.0, "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, + "minNotional": 2000000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "500000", + "notionalCap": "4000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.25", - "cum": "83675.0" + "cum": "334505.0" } }, { "tier": 9.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 4000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "8000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.5", - "cum": "333675.0" + "cum": "1334505.0" } } ], @@ -40932,128 +41464,144 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "75", + "notionalCap": "10000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 50000.0, + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 150000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { - "bracket": "2", + "bracket": "3", "initialLeverage": "25", - "notionalCap": "50000", - "notionalFloor": "5000", + "notionalCap": "150000", + "notionalFloor": "30000", "maintMarginRatio": "0.02", - "cum": "25.0" + "cum": "200.0" } }, { - "tier": 3.0, + "tier": 4.0, "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, + "minNotional": 150000.0, + "maxNotional": 300000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { - "bracket": "3", + "bracket": "4", "initialLeverage": "20", - "notionalCap": "100000", - "notionalFloor": "50000", + "notionalCap": "300000", + "notionalFloor": "150000", "maintMarginRatio": "0.025", - "cum": "275.0" + "cum": "950.0" } }, { - "tier": 4.0, + "tier": 5.0, "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 1000000.0, + "minNotional": 300000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "4", + "bracket": "5", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "100000", + "notionalCap": "1500000", + "notionalFloor": "300000", "maintMarginRatio": "0.05", - "cum": "2775.0" + "cum": "8450.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 1500000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "5", + "bracket": "6", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "3000000", + "notionalFloor": "1500000", "maintMarginRatio": "0.1", - "cum": "52775.0" + "cum": "83450.0" } }, { - "tier": 6.0, + "tier": 7.0, "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 2500000.0, + "minNotional": 3000000.0, + "maxNotional": 3750000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "6", + "bracket": "7", "initialLeverage": "4", - "notionalCap": "2500000", - "notionalFloor": "2000000", + "notionalCap": "3750000", + "notionalFloor": "3000000", "maintMarginRatio": "0.125", - "cum": "102775.0" + "cum": "158450.0" } }, { - "tier": 7.0, + "tier": 8.0, "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 3750000.0, + "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "7", + "bracket": "8", "initialLeverage": "2", - "notionalCap": "5000000", - "notionalFloor": "2500000", + "notionalCap": "7500000", + "notionalFloor": "3750000", "maintMarginRatio": "0.25", - "cum": "415275.0" + "cum": "627200.0" } }, { - "tier": 8.0, + "tier": 9.0, "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 7500000.0, + "maxNotional": 15000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "9", "initialLeverage": "1", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "15000000", + "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "1665275.0" + "cum": "2502200.0" } } ], From 6024903bdeb165afaae28d6cf3b04db704bfcac2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 13 Sep 2024 07:16:05 +0200 Subject: [PATCH 64/93] feat: conditionally apply retrier to market-reload closes #10641 --- freqtrade/exchange/exchange.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 684eaa5d8a8..9b35289c698 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -623,11 +623,21 @@ def ws_connection_reset(self): if self._exchange_ws: self._exchange_ws.reset_connections() + async def _api_reload_markets(self, reload: bool = False) -> None: + try: + return await self._api_async.load_markets(reload=reload, params={}) + except ccxt.DDoSProtection as e: + raise DDosProtection(e) from e + except (ccxt.OperationFailed, ccxt.ExchangeError) as e: + raise TemporaryError( + f"Error in reload_markets due to {e.__class__.__name__}. Message: {e}" + ) from e + except ccxt.BaseError as e: + raise TemporaryError(e) from e + def _load_async_markets(self, reload: bool = False) -> Dict[str, Any]: try: - markets = self.loop.run_until_complete( - self._api_async.load_markets(reload=reload, params={}) - ) + markets = self.loop.run_until_complete(self._api_reload_markets(reload=reload)) if isinstance(markets, Exception): raise markets @@ -652,7 +662,12 @@ def reload_markets(self, force: bool = False, *, load_leverage_tiers: bool = Tru logger.debug("Performing scheduled market reload..") try: # Reload async markets, then assign them to sync api - self._markets = self._load_async_markets(reload=True) + if force: + # Force reload of markets - retry several times + self._markets = retrier(self._load_async_markets, retries=3)(reload=True) + else: + # Normal market reload - accept temporary errors and use "old" markets + self._markets = self._load_async_markets(reload=True) self._api.set_markets(self._api_async.markets, self._api_async.currencies) # Assign options array, as it contains some temporary information from the exchange. self._api.options = self._api_async.options From 11eaa6d77cd461e0ade383a982d1a47733abd544 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 13 Sep 2024 07:16:51 +0200 Subject: [PATCH 65/93] test: Add tests for new behavior --- tests/exchange/test_exchange.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 6c9a1a9ba83..212c65e542c 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -567,7 +567,15 @@ def test__load_async_markets(default_conf, mocker, caplog): caplog.set_level(logging.DEBUG) exchange._api_async.load_markets = get_mock_coro(side_effect=ccxt.BaseError("deadbeef")) - with pytest.raises(ccxt.BaseError, match="deadbeef"): + with pytest.raises(TemporaryError, match="deadbeef"): + exchange._load_async_markets() + + exchange._api_async.load_markets = get_mock_coro(side_effect=ccxt.DDoSProtection("deadbeef")) + with pytest.raises(DDosProtection, match="deadbeef"): + exchange._load_async_markets() + + exchange._api_async.load_markets = get_mock_coro(side_effect=ccxt.OperationFailed("deadbeef")) + with pytest.raises(TemporaryError, match="deadbeef"): exchange._load_async_markets() From 5112736385f9b5d0cca43480c040a81469229039 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 13 Sep 2024 07:19:15 +0200 Subject: [PATCH 66/93] feat: Simplify reload_markets logic --- freqtrade/exchange/exchange.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 9b35289c698..0b73cd6e686 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -661,13 +661,10 @@ def reload_markets(self, force: bool = False, *, load_leverage_tiers: bool = Tru return None logger.debug("Performing scheduled market reload..") try: + # on initial load, we retry 3 times to ensure we get the markets + retries = 3 if force else 1 # Reload async markets, then assign them to sync api - if force: - # Force reload of markets - retry several times - self._markets = retrier(self._load_async_markets, retries=3)(reload=True) - else: - # Normal market reload - accept temporary errors and use "old" markets - self._markets = self._load_async_markets(reload=True) + self._markets = retrier(self._load_async_markets, retries=retries)(reload=True) self._api.set_markets(self._api_async.markets, self._api_async.currencies) # Assign options array, as it contains some temporary information from the exchange. self._api.options = self._api_async.options From c04cf6c5cb48a7071b395e48d0a401c1d269cac7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 13 Sep 2024 07:23:46 +0200 Subject: [PATCH 67/93] test: Improve test coverage of retry/fail logic --- freqtrade/exchange/exchange.py | 2 +- tests/exchange/test_exchange.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 0b73cd6e686..30c2aae6d4c 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -662,7 +662,7 @@ def reload_markets(self, force: bool = False, *, load_leverage_tiers: bool = Tru logger.debug("Performing scheduled market reload..") try: # on initial load, we retry 3 times to ensure we get the markets - retries = 3 if force else 1 + retries = 3 if force else 0 # Reload async markets, then assign them to sync api self._markets = retrier(self._load_async_markets, retries=retries)(reload=True) self._api.set_markets(self._api_async.markets, self._api_async.currencies) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 212c65e542c..35cc82db11b 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -638,6 +638,21 @@ def test_reload_markets(default_conf, mocker, caplog, time_machine): exchange.reload_markets() assert lam_spy.call_count == 0 + # Another reload should happen but it fails. + time_machine.move_to(start_dt + timedelta(minutes=51), tick=False) + api_mock.load_markets = get_mock_coro(side_effect=ccxt.NetworkError("LoadError")) + + exchange.reload_markets(force=False) + assert exchange.markets == updated_markets + assert lam_spy.call_count == 1 + # Tried once, failed + + lam_spy.reset_mock() + # When forceing (bot startup), it should retry 3 times. + exchange.reload_markets(force=True) + assert lam_spy.call_count == 4 + assert exchange.markets == updated_markets + def test_reload_markets_exception(default_conf, mocker, caplog): caplog.set_level(logging.DEBUG) From 9f34153c8424044cf1925d73d6827fced8a93624 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 13 Sep 2024 19:45:30 +0200 Subject: [PATCH 68/93] chore: update typing for reload function --- freqtrade/exchange/common.py | 4 ++++ freqtrade/exchange/exchange.py | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/common.py b/freqtrade/exchange/common.py index cac86ab3cfe..1bb738dcb56 100644 --- a/freqtrade/exchange/common.py +++ b/freqtrade/exchange/common.py @@ -164,6 +164,10 @@ async def wrapper(*args, **kwargs): def retrier(_func: F) -> F: ... +@overload +def retrier(_func: F, *, retries=API_RETRY_COUNT) -> F: ... + + @overload def retrier(*, retries=API_RETRY_COUNT) -> Callable[[F], F]: ... diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 30c2aae6d4c..6e5b2720bd2 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -623,7 +623,7 @@ def ws_connection_reset(self): if self._exchange_ws: self._exchange_ws.reset_connections() - async def _api_reload_markets(self, reload: bool = False) -> None: + async def _api_reload_markets(self, reload: bool = False) -> Dict[str, Any]: try: return await self._api_async.load_markets(reload=reload, params={}) except ccxt.DDoSProtection as e: @@ -662,7 +662,7 @@ def reload_markets(self, force: bool = False, *, load_leverage_tiers: bool = Tru logger.debug("Performing scheduled market reload..") try: # on initial load, we retry 3 times to ensure we get the markets - retries = 3 if force else 0 + retries: int = 3 if force else 0 # Reload async markets, then assign them to sync api self._markets = retrier(self._load_async_markets, retries=retries)(reload=True) self._api.set_markets(self._api_async.markets, self._api_async.currencies) From 0f505c6d7b3a1f3cc71a2e19b9289243656aa110 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Sep 2024 10:04:28 +0200 Subject: [PATCH 69/93] Improve check to cover more potential api oddities --- freqtrade/exchange/kraken.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py index 553e61ff8c4..d3e4dfcffe4 100644 --- a/freqtrade/exchange/kraken.py +++ b/freqtrade/exchange/kraken.py @@ -78,7 +78,7 @@ def get_balances(self) -> CcxtBalances: # x["side"], x["amount"], ) for x in orders - if x["price"] is not None + if (x["price"] is not None or x["side"] != "sell") and x["remaining"] is not None ] for bal in balances: if not isinstance(balances[bal], dict): From 51bdecea530d63aa97b58616c58eaea523918098 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 14 Sep 2024 10:04:28 +0200 Subject: [PATCH 70/93] Improve check to cover more potential api oddities --- freqtrade/exchange/kraken.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py index d3e4dfcffe4..9df9836b057 100644 --- a/freqtrade/exchange/kraken.py +++ b/freqtrade/exchange/kraken.py @@ -78,7 +78,7 @@ def get_balances(self) -> CcxtBalances: # x["side"], x["amount"], ) for x in orders - if (x["price"] is not None or x["side"] != "sell") and x["remaining"] is not None + if x["remaining"] is not None and (x["side"] == "sell" or x["price"] is not None) ] for bal in balances: if not isinstance(balances[bal], dict): From c67a9d4e846e69335587c004c417eb936cbc560f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 15 Sep 2024 09:29:45 +0200 Subject: [PATCH 71/93] docs: update pairlist creation docs --- docs/developer.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/developer.md b/docs/developer.md index e2f7766bb7c..127e8e5d570 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -205,7 +205,7 @@ This is called with each iteration of the bot (only if the Pairlist Handler is a It must return the resulting pairlist (which may then be passed into the chain of Pairlist Handlers). -Validations are optional, the parent class exposes a `_verify_blacklist(pairlist)` and `_whitelist_for_active_markets(pairlist)` to do default filtering. Use this if you limit your result to a certain number of pairs - so the end-result is not shorter than expected. +Validations are optional, the parent class exposes a `verify_blacklist(pairlist)` and `_whitelist_for_active_markets(pairlist)` to do default filtering. Use this if you limit your result to a certain number of pairs - so the end-result is not shorter than expected. #### filter_pairlist @@ -219,7 +219,7 @@ The default implementation in the base class simply calls the `_validate_pair()` If overridden, it must return the resulting pairlist (which may then be passed into the next Pairlist Handler in the chain). -Validations are optional, the parent class exposes a `_verify_blacklist(pairlist)` and `_whitelist_for_active_markets(pairlist)` to do default filters. Use this if you limit your result to a certain number of pairs - so the end result is not shorter than expected. +Validations are optional, the parent class exposes a `verify_blacklist(pairlist)` and `_whitelist_for_active_markets(pairlist)` to do default filters. Use this if you limit your result to a certain number of pairs - so the end result is not shorter than expected. In `VolumePairList`, this implements different methods of sorting, does early validation so only the expected number of pairs is returned. From 12299d481064d2eff6a83536c9c24c729f0533bd Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 15 Sep 2024 09:46:47 +0200 Subject: [PATCH 72/93] feat: staticPairlist to warn for invalid pairs Warnings about invalid pairs were "covered" by the implicit filtering of `expand_pairlist()` --- freqtrade/plugins/pairlist/StaticPairList.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/freqtrade/plugins/pairlist/StaticPairList.py b/freqtrade/plugins/pairlist/StaticPairList.py index 0591f4f19b1..5c7ee7e6252 100644 --- a/freqtrade/plugins/pairlist/StaticPairList.py +++ b/freqtrade/plugins/pairlist/StaticPairList.py @@ -61,14 +61,13 @@ def gen_pairlist(self, tickers: Tickers) -> List[str]: :param tickers: Tickers (from exchange.get_tickers). May be cached. :return: List of pairs """ + wl = self.verify_whitelist( + self._config["exchange"]["pair_whitelist"], logger.info, keep_invalid=True + ) if self._allow_inactive: - return self.verify_whitelist( - self._config["exchange"]["pair_whitelist"], logger.info, keep_invalid=True - ) + return wl else: - return self._whitelist_for_active_markets( - self.verify_whitelist(self._config["exchange"]["pair_whitelist"], logger.info) - ) + return self._whitelist_for_active_markets(wl) def filter_pairlist(self, pairlist: List[str], tickers: Tickers) -> List[str]: """ From bfb14614ccf7c93df117d20f77a11824f973887b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 15 Sep 2024 09:48:44 +0200 Subject: [PATCH 73/93] chore: enhance change with comment --- freqtrade/plugins/pairlist/StaticPairList.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/freqtrade/plugins/pairlist/StaticPairList.py b/freqtrade/plugins/pairlist/StaticPairList.py index 5c7ee7e6252..6a493a5c5a9 100644 --- a/freqtrade/plugins/pairlist/StaticPairList.py +++ b/freqtrade/plugins/pairlist/StaticPairList.py @@ -67,6 +67,8 @@ def gen_pairlist(self, tickers: Tickers) -> List[str]: if self._allow_inactive: return wl else: + # Avoid implicit filtering of "verify_whitelist" to keep + # proper warnings in the log return self._whitelist_for_active_markets(wl) def filter_pairlist(self, pairlist: List[str], tickers: Tickers) -> List[str]: From 95c250ebcce62686af6dae663a5186d76158c47f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 15 Sep 2024 10:37:28 +0200 Subject: [PATCH 74/93] chore: add explaining comment --- freqtrade/plugins/pairlist/pairlist_helpers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/plugins/pairlist/pairlist_helpers.py b/freqtrade/plugins/pairlist/pairlist_helpers.py index 9bbd851824d..cbe79c5f593 100644 --- a/freqtrade/plugins/pairlist/pairlist_helpers.py +++ b/freqtrade/plugins/pairlist/pairlist_helpers.py @@ -28,6 +28,7 @@ def expand_pairlist( except re.error as err: raise ValueError(f"Wildcard error in {pair_wc}, {err}") + # Remove wildcard pairs that didn't have a match. result = [element for element in result if re.fullmatch(r"^[A-Za-z0-9:/-]+$", element)] else: From 79020bba28e37fc1ed5b506dd60a76766522fca4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 15 Sep 2024 10:49:26 +0200 Subject: [PATCH 75/93] chore: Remove "prohibitedIn" check it's only been used for bitrex, which does no longer exist. apparently this was forgotten when decomissioning bittrex. --- freqtrade/exchange/exchange.py | 10 ---------- tests/exchange/test_exchange.py | 24 ------------------------ 2 files changed, 34 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 6e5b2720bd2..0f8e321c949 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -729,16 +729,6 @@ def validate_pairs(self, pairs: List[str]) -> None: # The internal info array is different for each particular market, # its contents depend on the exchange. # It can also be a string or similar ... so we need to verify that first. - elif isinstance(self.markets[pair].get("info"), dict) and self.markets[pair].get( - "info", {} - ).get("prohibitedIn", False): - # Warn users about restricted pairs in whitelist. - # We cannot determine reliably if Users are affected. - logger.warning( - f"Pair {pair} is restricted for some users on this exchange." - f"Please check if you are impacted by this restriction " - f"on the exchange and eventually remove {pair} from your whitelist." - ) if ( self._config["stake_currency"] and self.get_pair_quote_currency(pair) != self._config["stake_currency"] diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 35cc82db11b..187cb011ba0 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -812,30 +812,6 @@ def test_validate_pairs_exception(default_conf, mocker, caplog): assert log_has("Unable to validate pairs (assuming they are correct).", caplog) -def test_validate_pairs_restricted(default_conf, mocker, caplog): - api_mock = MagicMock() - type(api_mock).load_markets = get_mock_coro( - return_value={ - "ETH/BTC": {"quote": "BTC"}, - "LTC/BTC": {"quote": "BTC"}, - "XRP/BTC": {"quote": "BTC", "info": {"prohibitedIn": ["US"]}}, - "NEO/BTC": {"quote": "BTC", "info": "TestString"}, # info can also be a string ... - } - ) - mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) - mocker.patch(f"{EXMS}.validate_timeframes") - mocker.patch(f"{EXMS}.validate_pricing") - mocker.patch(f"{EXMS}.validate_stakecurrency") - - Exchange(default_conf) - assert log_has( - "Pair XRP/BTC is restricted for some users on this exchange." - "Please check if you are impacted by this restriction " - "on the exchange and eventually remove XRP/BTC from your whitelist.", - caplog, - ) - - def test_validate_pairs_stakecompatibility(default_conf, mocker): api_mock = MagicMock() type(api_mock).load_markets = get_mock_coro( From 7ebe1b8c148699c1412570bcb4982c5f561255a8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 15 Sep 2024 11:02:49 +0200 Subject: [PATCH 76/93] chore: remove pointless validation pairs are validated through expand_pairlist. If they're not in markets, they'll no longer be in the pairlist once this function function is hit. --- freqtrade/data/history/history_utils.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/freqtrade/data/history/history_utils.py b/freqtrade/data/history/history_utils.py index bbc9ec4d71d..9deab401ef1 100644 --- a/freqtrade/data/history/history_utils.py +++ b/freqtrade/data/history/history_utils.py @@ -610,9 +610,6 @@ def download_data_main(config: Config) -> None: if "timeframes" not in config: config["timeframes"] = DL_DATA_TIMEFRAMES - # Manual validations of relevant settings - if not config["exchange"].get("skip_pair_validation", False): - exchange.validate_pairs(expanded_pairs) logger.info( f"About to download pairs: {expanded_pairs}, " f"intervals: {config['timeframes']} to {config['datadir']}" From 94ef4380d44ad5a0261c884c69751fd9d21e72cb Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 15 Sep 2024 11:27:07 +0200 Subject: [PATCH 77/93] chore: remove validate_pairs from exchange class Invalid pairs were filtered out before this was called in most cases. in cases where it's not - regular pairlist-filtering provides proper warnings. --- freqtrade/exchange/exchange.py | 41 ---------------------------------- 1 file changed, 41 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 0f8e321c949..a63a8b6a0af 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -104,7 +104,6 @@ file_load_json, safe_value_fallback2, ) -from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist from freqtrade.util import dt_from_ts, dt_now from freqtrade.util.datetime_helpers import dt_humanize_delta, dt_ts, format_ms_time from freqtrade.util.periodic_cache import PeriodicCache @@ -331,8 +330,6 @@ def validate_config(self, config: Config) -> None: # Check if all pairs are available self.validate_stakecurrency(config["stake_currency"]) - if not config["exchange"].get("skip_pair_validation"): - self.validate_pairs(config["exchange"]["pair_whitelist"]) self.validate_ordertypes(config.get("order_types", {})) self.validate_order_time_in_force(config.get("order_time_in_force", {})) self.validate_trading_mode_and_margin_mode(self.trading_mode, self.margin_mode) @@ -702,44 +699,6 @@ def validate_stakecurrency(self, stake_currency: str) -> None: f"Available currencies are: {', '.join(quote_currencies)}" ) - def validate_pairs(self, pairs: List[str]) -> None: - """ - Checks if all given pairs are tradable on the current exchange. - :param pairs: list of pairs - :raise: OperationalException if one pair is not available - :return: None - """ - - if not self.markets: - logger.warning("Unable to validate pairs (assuming they are correct).") - return - extended_pairs = expand_pairlist(pairs, list(self.markets), keep_invalid=True) - invalid_pairs = [] - for pair in extended_pairs: - # Note: ccxt has BaseCurrency/QuoteCurrency format for pairs - if self.markets and pair not in self.markets: - raise OperationalException( - f"Pair {pair} is not available on {self.name} {self.trading_mode}. " - f"Please remove {pair} from your whitelist." - ) - - # From ccxt Documentation: - # markets.info: An associative array of non-common market properties, - # including fees, rates, limits and other general market information. - # The internal info array is different for each particular market, - # its contents depend on the exchange. - # It can also be a string or similar ... so we need to verify that first. - if ( - self._config["stake_currency"] - and self.get_pair_quote_currency(pair) != self._config["stake_currency"] - ): - invalid_pairs.append(pair) - if invalid_pairs: - raise OperationalException( - f"Stake-currency '{self._config['stake_currency']}' not compatible with " - f"pair-whitelist. Please remove the following pairs: {invalid_pairs}" - ) - def get_valid_pair_combination(self, curr_1: str, curr_2: str) -> str: """ Get valid pair combination of curr_1 and curr_2 by trying both combinations. From f4881e7c6ffbd8f53d6a3971c8f861451ca0f61b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 15 Sep 2024 11:27:23 +0200 Subject: [PATCH 78/93] tests: Adjust tests for removed validate_pairlist functionality --- tests/exchange/test_exchange.py | 135 ------------------------ tests/freqtradebot/test_freqtradebot.py | 1 - 2 files changed, 136 deletions(-) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 187cb011ba0..3114a340838 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -255,7 +255,6 @@ def test_init_exception(default_conf, mocker): def test_exchange_resolver(default_conf, mocker, caplog): mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=MagicMock())) mocker.patch(f"{EXMS}._load_async_markets") - mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_timeframes") mocker.patch(f"{EXMS}.validate_stakecurrency") mocker.patch(f"{EXMS}.validate_pricing") @@ -555,7 +554,6 @@ def test_get_min_pair_stake_amount_real_data(mocker, default_conf) -> None: def test__load_async_markets(default_conf, mocker, caplog): mocker.patch(f"{EXMS}._init_ccxt") - mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_timeframes") mocker.patch(f"{EXMS}.reload_markets") mocker.patch(f"{EXMS}.validate_stakecurrency") @@ -584,7 +582,6 @@ def test__load_markets(default_conf, mocker, caplog): api_mock = MagicMock() api_mock.load_markets = get_mock_coro(side_effect=ccxt.BaseError("SomeError")) mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) - mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_timeframes") mocker.patch(f"{EXMS}.validate_stakecurrency") mocker.patch(f"{EXMS}.validate_pricing") @@ -684,7 +681,6 @@ def test_validate_stakecurrency(default_conf, stake_currency, mocker, caplog): } ) mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) - mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_timeframes") mocker.patch(f"{EXMS}.validate_pricing") Exchange(default_conf) @@ -702,7 +698,6 @@ def test_validate_stakecurrency_error(default_conf, mocker, caplog): } ) mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) - mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_timeframes") with pytest.raises( ConfigurationError, @@ -755,123 +750,6 @@ def test_get_pair_base_currency(default_conf, mocker, pair, expected): assert ex.get_pair_base_currency(pair) == expected -def test_validate_pairs(default_conf, mocker): - api_mock = MagicMock() - id_mock = PropertyMock(return_value="test_exchange") - type(api_mock).id = id_mock - - mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) - mocker.patch(f"{EXMS}.validate_timeframes") - mocker.patch( - f"{EXMS}._load_async_markets", - return_value={ - "ETH/BTC": {"quote": "BTC"}, - "LTC/BTC": {"quote": "BTC"}, - "XRP/BTC": {"quote": "BTC"}, - "NEO/BTC": {"quote": "BTC"}, - }, - ) - mocker.patch(f"{EXMS}.validate_stakecurrency") - mocker.patch(f"{EXMS}.validate_pricing") - # test exchange.validate_pairs directly - # No assert - but this should not fail (!) - Exchange(default_conf) - - -def test_validate_pairs_not_available(default_conf, mocker): - api_mock = MagicMock() - type(api_mock).markets = PropertyMock( - return_value={"XRP/BTC": {"inactive": True, "base": "XRP", "quote": "BTC"}} - ) - mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) - mocker.patch(f"{EXMS}.validate_timeframes") - mocker.patch(f"{EXMS}.validate_stakecurrency") - mocker.patch(f"{EXMS}._load_async_markets") - - with pytest.raises(OperationalException, match=r"not available"): - Exchange(default_conf) - - -def test_validate_pairs_exception(default_conf, mocker, caplog): - caplog.set_level(logging.INFO) - api_mock = MagicMock() - mocker.patch(f"{EXMS}.name", PropertyMock(return_value="Binance")) - - type(api_mock).markets = PropertyMock(return_value={}) - mocker.patch(f"{EXMS}._init_ccxt", api_mock) - mocker.patch(f"{EXMS}.validate_timeframes") - mocker.patch(f"{EXMS}.validate_stakecurrency") - mocker.patch(f"{EXMS}.validate_pricing") - mocker.patch(f"{EXMS}._load_async_markets") - - with pytest.raises(OperationalException, match=r"Pair ETH/BTC is not available on Binance"): - Exchange(default_conf) - - mocker.patch(f"{EXMS}.markets", PropertyMock(return_value={})) - Exchange(default_conf) - assert log_has("Unable to validate pairs (assuming they are correct).", caplog) - - -def test_validate_pairs_stakecompatibility(default_conf, mocker): - api_mock = MagicMock() - type(api_mock).load_markets = get_mock_coro( - return_value={ - "ETH/BTC": {"quote": "BTC"}, - "LTC/BTC": {"quote": "BTC"}, - "XRP/BTC": {"quote": "BTC"}, - "NEO/BTC": {"quote": "BTC"}, - "HELLO-WORLD": {"quote": "BTC"}, - } - ) - mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) - mocker.patch(f"{EXMS}.validate_timeframes") - mocker.patch(f"{EXMS}.validate_stakecurrency") - mocker.patch(f"{EXMS}.validate_pricing") - - Exchange(default_conf) - - -def test_validate_pairs_stakecompatibility_downloaddata(default_conf, mocker): - api_mock = MagicMock() - default_conf["stake_currency"] = "" - type(api_mock).load_markets = get_mock_coro( - return_value={ - "ETH/BTC": {"quote": "BTC"}, - "LTC/BTC": {"quote": "BTC"}, - "XRP/BTC": {"quote": "BTC"}, - "NEO/BTC": {"quote": "BTC"}, - "HELLO-WORLD": {"quote": "BTC"}, - } - ) - mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) - mocker.patch(f"{EXMS}.validate_timeframes") - mocker.patch(f"{EXMS}.validate_stakecurrency") - mocker.patch(f"{EXMS}.validate_pricing") - - Exchange(default_conf) - assert type(api_mock).load_markets.call_count == 1 - - -def test_validate_pairs_stakecompatibility_fail(default_conf, mocker): - default_conf["exchange"]["pair_whitelist"].append("HELLO-WORLD") - api_mock = MagicMock() - type(api_mock).load_markets = get_mock_coro( - return_value={ - "ETH/BTC": {"quote": "BTC"}, - "LTC/BTC": {"quote": "BTC"}, - "XRP/BTC": {"quote": "BTC"}, - "NEO/BTC": {"quote": "BTC"}, - "HELLO-WORLD": {"quote": "USDT"}, - } - ) - mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) - mocker.patch(f"{EXMS}.validate_timeframes") - mocker.patch(f"{EXMS}.validate_stakecurrency") - - with pytest.raises(OperationalException, match=r"Stake-currency 'BTC' not compatible with.*"): - Exchange(default_conf) - - @pytest.mark.parametrize("timeframe", [("5m"), ("1m"), ("15m"), ("1h")]) def test_validate_timeframes(default_conf, mocker, timeframe): default_conf["timeframe"] = timeframe @@ -883,7 +761,6 @@ def test_validate_timeframes(default_conf, mocker, timeframe): mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) mocker.patch(f"{EXMS}.reload_markets") - mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_stakecurrency") mocker.patch(f"{EXMS}.validate_pricing") Exchange(default_conf) @@ -901,7 +778,6 @@ def test_validate_timeframes_failed(default_conf, mocker): mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) mocker.patch(f"{EXMS}.reload_markets") - mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_stakecurrency") mocker.patch(f"{EXMS}.validate_pricing") with pytest.raises( @@ -931,7 +807,6 @@ def test_validate_timeframes_emulated_ohlcv_1(default_conf, mocker): mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) mocker.patch(f"{EXMS}.reload_markets") - mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_stakecurrency") with pytest.raises( OperationalException, @@ -953,7 +828,6 @@ def test_validate_timeframes_emulated_ohlcvi_2(default_conf, mocker): mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) mocker.patch(f"{EXMS}.reload_markets") - mocker.patch(f"{EXMS}.validate_pairs", MagicMock()) mocker.patch(f"{EXMS}.validate_stakecurrency") with pytest.raises( OperationalException, @@ -975,7 +849,6 @@ def test_validate_timeframes_not_in_config(default_conf, mocker): mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) mocker.patch(f"{EXMS}.reload_markets") - mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_stakecurrency") mocker.patch(f"{EXMS}.validate_pricing") mocker.patch(f"{EXMS}.validate_required_startup_candles") @@ -992,7 +865,6 @@ def test_validate_pricing(default_conf, mocker): mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) mocker.patch(f"{EXMS}.reload_markets") mocker.patch(f"{EXMS}.validate_trading_mode_and_margin_mode") - mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_timeframes") mocker.patch(f"{EXMS}.validate_stakecurrency") mocker.patch(f"{EXMS}.name", "Binance") @@ -1027,7 +899,6 @@ def test_validate_ordertypes(default_conf, mocker): type(api_mock).has = PropertyMock(return_value={"createMarketOrder": True}) mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) mocker.patch(f"{EXMS}.reload_markets") - mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_timeframes") mocker.patch(f"{EXMS}.validate_stakecurrency") mocker.patch(f"{EXMS}.validate_pricing") @@ -1086,7 +957,6 @@ def test_validate_ordertypes_stop_advanced(default_conf, mocker, exchange_name, type(api_mock).has = PropertyMock(return_value={"createMarketOrder": True}) mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) mocker.patch(f"{EXMS}.reload_markets") - mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_timeframes") mocker.patch(f"{EXMS}.validate_stakecurrency") mocker.patch(f"{EXMS}.validate_pricing") @@ -1111,7 +981,6 @@ def test_validate_order_types_not_in_config(default_conf, mocker): api_mock = MagicMock() mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) mocker.patch(f"{EXMS}.reload_markets") - mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_timeframes") mocker.patch(f"{EXMS}.validate_pricing") mocker.patch(f"{EXMS}.validate_stakecurrency") @@ -1127,7 +996,6 @@ def test_validate_required_startup_candles(default_conf, mocker, caplog): mocker.patch(f"{EXMS}._init_ccxt", api_mock) mocker.patch(f"{EXMS}.validate_timeframes") mocker.patch(f"{EXMS}._load_async_markets") - mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_pricing") mocker.patch(f"{EXMS}.validate_stakecurrency") @@ -4161,7 +4029,6 @@ def test_merge_ft_has_dict(default_conf, mocker): EXMS, _init_ccxt=MagicMock(return_value=MagicMock()), _load_async_markets=MagicMock(), - validate_pairs=MagicMock(), validate_timeframes=MagicMock(), validate_stakecurrency=MagicMock(), validate_pricing=MagicMock(), @@ -4196,7 +4063,6 @@ def test_get_valid_pair_combination(default_conf, mocker, markets): EXMS, _init_ccxt=MagicMock(return_value=MagicMock()), _load_async_markets=MagicMock(), - validate_pairs=MagicMock(), validate_timeframes=MagicMock(), validate_pricing=MagicMock(), markets=PropertyMock(return_value=markets), @@ -4476,7 +4342,6 @@ def test_get_markets( EXMS, _init_ccxt=MagicMock(return_value=MagicMock()), _load_async_markets=MagicMock(), - validate_pairs=MagicMock(), validate_timeframes=MagicMock(), validate_pricing=MagicMock(), markets=PropertyMock(return_value=markets_static), diff --git a/tests/freqtradebot/test_freqtradebot.py b/tests/freqtradebot/test_freqtradebot.py index 7c45928a2eb..f0b2d5b36c8 100644 --- a/tests/freqtradebot/test_freqtradebot.py +++ b/tests/freqtradebot/test_freqtradebot.py @@ -2204,7 +2204,6 @@ def test_manage_open_orders_buy_exception( patch_exchange(mocker) mocker.patch.multiple( EXMS, - validate_pairs=MagicMock(), fetch_ticker=ticker_usdt, fetch_order=MagicMock(side_effect=ExchangeError), cancel_order=cancel_order_mock, From ae41ab101a0296a31729b00add3f4adb7682ce9c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 15 Sep 2024 11:27:38 +0200 Subject: [PATCH 79/93] docs: remove skip_pair_validation - it's no longer used. --- docs/configuration.md | 1 - docs/includes/pairlists.md | 1 - 2 files changed, 2 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index aed34762b9c..b05b1dcaada 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -222,7 +222,6 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `exchange.ccxt_async_config` | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://docs.ccxt.com/#/README?id=overriding-exchange-properties-upon-instantiation)
**Datatype:** Dict | `exchange.enable_ws` | Enable the usage of Websockets for the exchange.
[More information](#consuming-exchange-websockets).
*Defaults to `true`.*
**Datatype:** Boolean | `exchange.markets_refresh_interval` | The interval in minutes in which markets are reloaded.
*Defaults to `60` minutes.*
**Datatype:** Positive Integer -| `exchange.skip_pair_validation` | Skip pairlist validation on startup.
*Defaults to `false`*
**Datatype:** Boolean | `exchange.skip_open_order_update` | Skips open order updates on startup should the exchange cause problems. Only relevant in live conditions.
*Defaults to `false`*
**Datatype:** Boolean | `exchange.unknown_fee_rate` | Fallback value to use when calculating trading fees. This can be useful for exchanges which have fees in non-tradable currencies. The value provided here will be multiplied with the "fee cost".
*Defaults to `None`
**Datatype:** float | `exchange.log_responses` | Log relevant exchange responses. For debug mode only - use with care.
*Defaults to `false`*
**Datatype:** Boolean diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index b3b69f99636..804190e248a 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -55,7 +55,6 @@ It uses configuration from `exchange.pair_whitelist` and `exchange.pair_blacklis By default, only currently enabled pairs are allowed. To skip pair validation against active markets, set `"allow_inactive": true` within the `StaticPairList` configuration. This can be useful for backtesting expired pairs (like quarterly spot-markets). -This option must be configured along with `exchange.skip_pair_validation` in the exchange configuration. When used in a "follow-up" position (e.g. after VolumePairlist), all pairs in `'pair_whitelist'` will be added to the end of the pairlist. From ad8e6e7d67cfe11e107e0fb74fc1a18683594bb6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 03:15:58 +0000 Subject: [PATCH 80/93] chore(deps-dev): bump types-requests in the types group Bumps the types group with 1 update: [types-requests](https://github.com/python/typeshed). Updates `types-requests` from 2.32.0.20240907 to 2.32.0.20240914 - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-requests dependency-type: direct:development update-type: version-update:semver-patch dependency-group: types ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 08fed3d0202..5f9250c293a 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -27,6 +27,6 @@ nbconvert==7.16.4 # mypy types types-cachetools==5.5.0.20240820 types-filelock==3.2.7 -types-requests==2.32.0.20240907 +types-requests==2.32.0.20240914 types-tabulate==0.9.0.20240106 types-python-dateutil==2.9.0.20240906 From cf3af42477f84459116dcff89f62fd68ccbbe7f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 03:16:39 +0000 Subject: [PATCH 81/93] chore(deps-dev): bump pytest from 8.3.2 to 8.3.3 in the pytest group Bumps the pytest group with 1 update: [pytest](https://github.com/pytest-dev/pytest). Updates `pytest` from 8.3.2 to 8.3.3 - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/8.3.2...8.3.3) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:development update-type: version-update:semver-patch dependency-group: pytest ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 08fed3d0202..0ea0a7d081b 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -10,7 +10,7 @@ coveralls==4.0.1 ruff==0.6.4 mypy==1.11.2 pre-commit==3.8.0 -pytest==8.3.2 +pytest==8.3.3 pytest-asyncio==0.24.0 pytest-cov==5.0.0 pytest-mock==3.14.0 From d7a9841328b28763349211a7cd302e382da5d9b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 03:17:01 +0000 Subject: [PATCH 82/93] chore(deps): bump plotly from 5.24.0 to 5.24.1 Bumps [plotly](https://github.com/plotly/plotly.py) from 5.24.0 to 5.24.1. - [Release notes](https://github.com/plotly/plotly.py/releases) - [Changelog](https://github.com/plotly/plotly.py/blob/master/CHANGELOG.md) - [Commits](https://github.com/plotly/plotly.py/compare/v5.24.0...v5.24.1) --- updated-dependencies: - dependency-name: plotly dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-plot.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-plot.txt b/requirements-plot.txt index 987447cb29f..a50d56ead14 100644 --- a/requirements-plot.txt +++ b/requirements-plot.txt @@ -1,4 +1,4 @@ # Include all requirements to run the bot. -r requirements.txt -plotly==5.24.0 +plotly==5.24.1 From d37405a30715343de01cb593d1d7cb48cc32d517 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 03:17:05 +0000 Subject: [PATCH 83/93] chore(deps): bump urllib3 from 2.2.2 to 2.2.3 Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.2.2 to 2.2.3. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.2.2...2.2.3) --- updated-dependencies: - dependency-name: urllib3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c23e8916336..3c3b1e9e833 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ httpx>=0.24.1 humanize==4.10.0 cachetools==5.5.0 requests==2.32.3 -urllib3==2.2.2 +urllib3==2.2.3 jsonschema==4.23.0 TA-Lib==0.4.32 technical==1.4.4 From c3b6f4ca855eaf7d7db479c9df214bda78e59472 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 03:17:21 +0000 Subject: [PATCH 84/93] chore(deps): bump pytz from 2024.1 to 2024.2 Bumps [pytz](https://github.com/stub42/pytz) from 2024.1 to 2024.2. - [Release notes](https://github.com/stub42/pytz/releases) - [Commits](https://github.com/stub42/pytz/compare/release_2024.1...release_2024.2) --- updated-dependencies: - dependency-name: pytz dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c23e8916336..d2270b9d9bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -53,7 +53,7 @@ questionary==2.0.1 prompt-toolkit==3.0.36 # Extensions to datetime library python-dateutil==2.9.0.post0 -pytz==2024.1 +pytz==2024.2 #Futures schedule==1.2.2 From 11d6ec33b3955a64a535f485b17ae837c7b88469 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 03:17:39 +0000 Subject: [PATCH 85/93] chore(deps): bump scikit-learn from 1.5.1 to 1.5.2 Bumps [scikit-learn](https://github.com/scikit-learn/scikit-learn) from 1.5.1 to 1.5.2. - [Release notes](https://github.com/scikit-learn/scikit-learn/releases) - [Commits](https://github.com/scikit-learn/scikit-learn/compare/1.5.1...1.5.2) --- updated-dependencies: - dependency-name: scikit-learn dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-freqai.txt | 2 +- requirements-hyperopt.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements-freqai.txt b/requirements-freqai.txt index 1731ac0546b..e71dfc1296f 100644 --- a/requirements-freqai.txt +++ b/requirements-freqai.txt @@ -3,7 +3,7 @@ -r requirements-plot.txt # Required for freqai -scikit-learn==1.5.1 +scikit-learn==1.5.2 joblib==1.4.2 catboost==1.2.7; 'arm' not in platform_machine # Pin Matplotlib - it's depended on by catboost diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 0daa19128ae..a8e8b557c78 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -4,6 +4,6 @@ # Required for hyperopt scipy==1.14.1; python_version >= "3.10" scipy==1.13.1; python_version < "3.10" -scikit-learn==1.5.1 +scikit-learn==1.5.2 ft-scikit-optimize==0.9.2 filelock==3.16.0 From e9ccc98ada48607d0945f774d247041ce9401e93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 03:17:46 +0000 Subject: [PATCH 86/93] chore(deps): bump fastapi from 0.114.0 to 0.114.2 Bumps [fastapi](https://github.com/fastapi/fastapi) from 0.114.0 to 0.114.2. - [Release notes](https://github.com/fastapi/fastapi/releases) - [Commits](https://github.com/fastapi/fastapi/compare/0.114.0...0.114.2) --- updated-dependencies: - dependency-name: fastapi dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c23e8916336..30130600680 100644 --- a/requirements.txt +++ b/requirements.txt @@ -41,7 +41,7 @@ orjson==3.10.7 sdnotify==0.3.2 # API Server -fastapi==0.114.0 +fastapi==0.114.2 pydantic==2.9.0 uvicorn==0.30.6 pyjwt==2.9.0 From db4c4b971a8ded49902575382ea00da8ea8e51a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 03:17:58 +0000 Subject: [PATCH 87/93] chore(deps): bump ccxt from 4.3.98 to 4.4.3 Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.3.98 to 4.4.3. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md) - [Commits](https://github.com/ccxt/ccxt/compare/4.3.98...4.4.3) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c23e8916336..d56439965ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ bottleneck==1.4.0 numexpr==2.10.1 pandas-ta==0.3.14b -ccxt==4.3.98 +ccxt==4.4.3 cryptography==42.0.8; platform_machine == 'armv7l' cryptography==43.0.1; platform_machine != 'armv7l' aiohttp==3.10.5 From c73fa2b0eb5611eb33930c43de65e92a47ddc227 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 03:18:09 +0000 Subject: [PATCH 88/93] chore(deps): bump rich from 13.8.0 to 13.8.1 Bumps [rich](https://github.com/Textualize/rich) from 13.8.0 to 13.8.1. - [Release notes](https://github.com/Textualize/rich/releases) - [Changelog](https://github.com/Textualize/rich/blob/master/CHANGELOG.md) - [Commits](https://github.com/Textualize/rich/compare/v13.8.0...v13.8.1) --- updated-dependencies: - dependency-name: rich dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c23e8916336..17debc5aa26 100644 --- a/requirements.txt +++ b/requirements.txt @@ -26,7 +26,7 @@ jinja2==3.1.4 tables==3.9.1; python_version < "3.10" tables==3.10.1; python_version >= "3.10" joblib==1.4.2 -rich==13.8.0 +rich==13.8.1 pyarrow==17.0.0; platform_machine != 'armv7l' # find first, C search in arrays From a7f46500ed5778a0b30da4eee7c7a4a15e377022 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 16 Sep 2024 06:38:45 +0200 Subject: [PATCH 89/93] chore: bump types-requests in pre-commit --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3fcde5a369a..490f09eed4e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: additional_dependencies: - types-cachetools==5.5.0.20240820 - types-filelock==3.2.7 - - types-requests==2.32.0.20240907 + - types-requests==2.32.0.20240914 - types-tabulate==0.9.0.20240106 - types-python-dateutil==2.9.0.20240906 - SQLAlchemy==2.0.34 From 09c14594117555c7cd824a7bd0d7d59a9efe0f8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 06:17:40 +0000 Subject: [PATCH 90/93] chore(deps-dev): bump ruff from 0.6.4 to 0.6.5 Bumps [ruff](https://github.com/astral-sh/ruff) from 0.6.4 to 0.6.5. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.6.4...0.6.5) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 0ea0a7d081b..06eb01833be 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,7 +7,7 @@ -r docs/requirements-docs.txt coveralls==4.0.1 -ruff==0.6.4 +ruff==0.6.5 mypy==1.11.2 pre-commit==3.8.0 pytest==8.3.3 From 65e6c737cd768895f835a05e9a67cc9495355cb1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 07:07:06 +0000 Subject: [PATCH 91/93] chore(deps): bump pydantic from 2.9.0 to 2.9.1 Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.9.0 to 2.9.1. - [Release notes](https://github.com/pydantic/pydantic/releases) - [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md) - [Commits](https://github.com/pydantic/pydantic/compare/v2.9.0...v2.9.1) --- updated-dependencies: - dependency-name: pydantic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6f1e67bbbac..06051839120 100644 --- a/requirements.txt +++ b/requirements.txt @@ -42,7 +42,7 @@ sdnotify==0.3.2 # API Server fastapi==0.114.2 -pydantic==2.9.0 +pydantic==2.9.1 uvicorn==0.30.6 pyjwt==2.9.0 aiofiles==24.1.0 From 2fe67edab3ecb4e76936b51547f1986bf4c54003 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 16 Sep 2024 19:05:00 +0200 Subject: [PATCH 92/93] chore: update link to okx liquidation formula --- freqtrade/exchange/exchange.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index a63a8b6a0af..47ff89f46e7 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -3594,7 +3594,7 @@ def dry_run_liquidation_price( Wherein, "+" or "-" depends on whether the contract goes long or short: "-" for long, and "+" for short. - okex: https://www.okex.com/support/hc/en-us/articles/ + okex: https://www.okx.com/support/hc/en-us/articles/ 360053909592-VI-Introduction-to-the-isolated-mode-of-Single-Multi-currency-Portfolio-margin :param pair: Pair to calculate liquidation price for From ff9d1f272863c496d5b2d43c1af6cffbff730098 Mon Sep 17 00:00:00 2001 From: xmatthias <5024695+xmatthias@users.noreply.github.com> Date: Tue, 17 Sep 2024 03:03:39 +0000 Subject: [PATCH 93/93] chore: update pre-commit hooks --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 490f09eed4e..4a287743e32 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -31,7 +31,7 @@ repos: - repo: https://github.com/charliermarsh/ruff-pre-commit # Ruff version. - rev: 'v0.6.4' + rev: 'v0.6.5' hooks: - id: ruff - id: ruff-format