From 2c9b7659539ddbf9063f69f4a06429c3790552fc Mon Sep 17 00:00:00 2001 From: Timothy Pogue Date: Wed, 7 Sep 2022 09:35:37 -0600 Subject: [PATCH 01/16] add suffix parameter --- freqtrade/strategy/strategy_helper.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/freqtrade/strategy/strategy_helper.py b/freqtrade/strategy/strategy_helper.py index 43728dc1f..55afbf7a8 100644 --- a/freqtrade/strategy/strategy_helper.py +++ b/freqtrade/strategy/strategy_helper.py @@ -1,3 +1,5 @@ +from typing import Optional + import pandas as pd from freqtrade.exchange import timeframe_to_minutes @@ -6,7 +8,8 @@ from freqtrade.exchange import timeframe_to_minutes def merge_informative_pair(dataframe: pd.DataFrame, informative: pd.DataFrame, timeframe: str, timeframe_inf: str, ffill: bool = True, append_timeframe: bool = True, - date_column: str = 'date') -> pd.DataFrame: + date_column: str = 'date', + suffix: Optional[str] = None) -> pd.DataFrame: """ Correctly merge informative samples to the original dataframe, avoiding lookahead bias. @@ -50,10 +53,17 @@ def merge_informative_pair(dataframe: pd.DataFrame, informative: pd.DataFrame, # Rename columns to be unique date_merge = 'date_merge' - if append_timeframe: + if append_timeframe and not suffix: date_merge = f'date_merge_{timeframe_inf}' informative.columns = [f"{col}_{timeframe_inf}" for col in informative.columns] + elif suffix: + date_merge = f'date_merge_{suffix}' + informative.columns = [f"{col}_{suffix}" for col in informative.columns] + + elif suffix and append_timeframe: + raise ValueError("You can not specify `append_timeframe` as True and a `suffix`.") + # Combine the 2 dataframes # all indicators on the informative sample MUST be calculated before this point if ffill: From ea7bdac9edc7b3f5c5e039e509b8d368f7045a3e Mon Sep 17 00:00:00 2001 From: robcaulk Date: Wed, 7 Sep 2022 18:45:16 +0200 Subject: [PATCH 02/16] ensure inlier metric can be combined with other cleaning methods --- freqtrade/freqai/freqai_interface.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/freqtrade/freqai/freqai_interface.py b/freqtrade/freqai/freqai_interface.py index 5ccc9d1b2..32e42e115 100644 --- a/freqtrade/freqai/freqai_interface.py +++ b/freqtrade/freqai/freqai_interface.py @@ -427,6 +427,11 @@ class IFreqaiModel(ABC): ft_params = self.freqai_info["feature_parameters"] + if ft_params.get('inlier_metric_window', 0): + dk.compute_inlier_metric(set_='train') + if self.freqai_info["data_split_parameters"]["test_size"] > 0: + dk.compute_inlier_metric(set_='test') + if ft_params.get( "principal_component_analysis", False ): @@ -446,11 +451,6 @@ class IFreqaiModel(ABC): dk.use_DBSCAN_to_remove_outliers(predict=False, eps=eps) self.dd.old_DBSCAN_eps[dk.pair] = dk.data['DBSCAN_eps'] - if ft_params.get('inlier_metric_window', 0): - dk.compute_inlier_metric(set_='train') - if self.freqai_info["data_split_parameters"]["test_size"] > 0: - dk.compute_inlier_metric(set_='test') - if self.freqai_info["feature_parameters"].get('noise_standard_deviation', 0): dk.add_noise_to_training_features() From e51d352777d9e06518da0f28c974dceabf4afd0c Mon Sep 17 00:00:00 2001 From: robcaulk Date: Wed, 7 Sep 2022 19:11:54 +0200 Subject: [PATCH 03/16] ensure pca is handling same DF as inlier --- freqtrade/freqai/freqai_interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqai/freqai_interface.py b/freqtrade/freqai/freqai_interface.py index 32e42e115..c5ac17a3a 100644 --- a/freqtrade/freqai/freqai_interface.py +++ b/freqtrade/freqai/freqai_interface.py @@ -467,7 +467,7 @@ class IFreqaiModel(ABC): if ft_params.get( "principal_component_analysis", False ): - dk.pca_transform(dataframe) + dk.pca_transform(self.dk.data_dictionary['prediction_features']) if ft_params.get("use_SVM_to_remove_outliers", False): dk.use_SVM_to_remove_outliers(predict=True) From 1ef1fc269e2b682a2d8053340e8349436a132269 Mon Sep 17 00:00:00 2001 From: Timothy Pogue Date: Wed, 7 Sep 2022 15:26:38 -0600 Subject: [PATCH 04/16] docstring and tests --- freqtrade/strategy/strategy_helper.py | 4 +++- tests/strategy/test_strategy_helpers.py | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/freqtrade/strategy/strategy_helper.py b/freqtrade/strategy/strategy_helper.py index 55afbf7a8..53f625001 100644 --- a/freqtrade/strategy/strategy_helper.py +++ b/freqtrade/strategy/strategy_helper.py @@ -31,6 +31,8 @@ def merge_informative_pair(dataframe: pd.DataFrame, informative: pd.DataFrame, :param ffill: Forwardfill missing values - optional but usually required :param append_timeframe: Rename columns by appending timeframe. :param date_column: A custom date column name. + :param suffix: A string suffix to add at the end of the informative columns. If specified, + append_timeframe must be false. :return: Merged dataframe :raise: ValueError if the secondary timeframe is shorter than the dataframe timeframe """ @@ -57,7 +59,7 @@ def merge_informative_pair(dataframe: pd.DataFrame, informative: pd.DataFrame, date_merge = f'date_merge_{timeframe_inf}' informative.columns = [f"{col}_{timeframe_inf}" for col in informative.columns] - elif suffix: + elif suffix and not append_timeframe: date_merge = f'date_merge_{suffix}' informative.columns = [f"{col}_{suffix}" for col in informative.columns] diff --git a/tests/strategy/test_strategy_helpers.py b/tests/strategy/test_strategy_helpers.py index a7c2da26a..8cb990e87 100644 --- a/tests/strategy/test_strategy_helpers.py +++ b/tests/strategy/test_strategy_helpers.py @@ -117,6 +117,29 @@ def test_merge_informative_pair_lower(): merge_informative_pair(data, informative, '1h', '15m', ffill=True) +def test_merge_informative_pair_suffix(): + data = generate_test_data('15m', 20) + informative = generate_test_data('1h', 20) + + result = merge_informative_pair(data, informative, '15m', '1h', + append_timeframe=False, suffix="suf") + + assert 'date' in result.columns + assert result['date'].equals(data['date']) + assert 'date_suf' in result.columns + + assert 'open_suf' in result.columns + assert 'open_1h' not in result.columns + + +def test_merge_informative_pair_suffix_append_timeframe(): + data = generate_test_data('15m', 20) + informative = generate_test_data('1h', 20) + + with pytest.raises(ValueError, match=r"You can not specify `append_timeframe` .*"): + merge_informative_pair(data, informative, '15m', '1h', suffix="suf") + + def test_stoploss_from_open(): open_price_ranges = [ [0.01, 1.00, 30], From f3417a869069bdb01da844017b00ebf30ee6f208 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 8 Sep 2022 06:59:14 +0200 Subject: [PATCH 05/16] Revert condition sequence to simplify conditions --- freqtrade/strategy/strategy_helper.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/freqtrade/strategy/strategy_helper.py b/freqtrade/strategy/strategy_helper.py index 53f625001..aa753a829 100644 --- a/freqtrade/strategy/strategy_helper.py +++ b/freqtrade/strategy/strategy_helper.py @@ -55,17 +55,16 @@ def merge_informative_pair(dataframe: pd.DataFrame, informative: pd.DataFrame, # Rename columns to be unique date_merge = 'date_merge' - if append_timeframe and not suffix: + if suffix and append_timeframe: + raise ValueError("You can not specify `append_timeframe` as True and a `suffix`.") + elif append_timeframe: date_merge = f'date_merge_{timeframe_inf}' informative.columns = [f"{col}_{timeframe_inf}" for col in informative.columns] - elif suffix and not append_timeframe: + elif suffix: date_merge = f'date_merge_{suffix}' informative.columns = [f"{col}_{suffix}" for col in informative.columns] - elif suffix and append_timeframe: - raise ValueError("You can not specify `append_timeframe` as True and a `suffix`.") - # Combine the 2 dataframes # all indicators on the informative sample MUST be calculated before this point if ffill: From 4e15611b055a7afb5dfc00fd687967fa52bca1bf Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 8 Sep 2022 07:18:38 +0200 Subject: [PATCH 06/16] Don't crash in case of funding fee fetch error --- freqtrade/exchange/exchange.py | 11 +++++-- freqtrade/freqtradebot.py | 57 ++++++++++++++++++++-------------- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 569dcad9b..be5af91db 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -2509,8 +2509,13 @@ class Exchange: cache=False, drop_incomplete=False, ) - funding_rates = candle_histories[funding_comb] - mark_rates = candle_histories[mark_comb] + try: + # we can't assume we always get histories - for example during exchange downtimes + funding_rates = candle_histories[funding_comb] + mark_rates = candle_histories[mark_comb] + except KeyError: + raise ExchangeError("Could not find funding rates") from None + funding_mark_rates = self.combine_funding_and_mark( funding_rates=funding_rates, mark_rates=mark_rates) @@ -2590,6 +2595,8 @@ class Exchange: :param is_short: trade direction :param amount: Trade amount :param open_date: Open date of the trade + :return: funding fee since open_date + :raies: ExchangeError if something goes wrong. """ if self.trading_mode == TradingMode.FUTURES: if self._config['dry_run']: diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index ec32cae0e..61c323ed3 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -281,14 +281,17 @@ class FreqtradeBot(LoggingMixin): def update_funding_fees(self): if self.trading_mode == TradingMode.FUTURES: trades = Trade.get_open_trades() - for trade in trades: - funding_fees = self.exchange.get_funding_fees( - pair=trade.pair, - amount=trade.amount, - is_short=trade.is_short, - open_date=trade.date_last_filled_utc - ) - trade.funding_fees = funding_fees + try: + for trade in trades: + funding_fees = self.exchange.get_funding_fees( + pair=trade.pair, + amount=trade.amount, + is_short=trade.is_short, + open_date=trade.date_last_filled_utc + ) + trade.funding_fees = funding_fees + except ExchangeError: + logger.warning("Could not update funding fees for open trades.") else: return 0.0 @@ -671,14 +674,12 @@ class FreqtradeBot(LoggingMixin): if not stake_amount: return False - if pos_adjust: - logger.info(f"Position adjust: about to create a new order for {pair} with stake: " - f"{stake_amount} for {trade}") - else: - logger.info( - f"{name} signal found: about create a new trade for {pair} with stake_amount: " - f"{stake_amount} ...") - + msg = (f"Position adjust: about to create a new order for {pair} with stake: " + f"{stake_amount} for {trade}" if pos_adjust + else + f"{name} signal found: about create a new trade for {pair} with stake_amount: " + f"{stake_amount} ...") + logger.info(msg) amount = (stake_amount / enter_limit_requested) * leverage order_type = ordertype or self.strategy.order_types['entry'] @@ -741,8 +742,12 @@ class FreqtradeBot(LoggingMixin): # This is a new trade if trade is None: - funding_fees = self.exchange.get_funding_fees( - pair=pair, amount=amount, is_short=is_short, open_date=open_date) + try: + funding_fees = self.exchange.get_funding_fees( + pair=pair, amount=amount, is_short=is_short, open_date=open_date) + except ExchangeError: + logger.warning("Could not update funding fee.") + trade = Trade( pair=pair, base_currency=base_currency, @@ -1493,12 +1498,16 @@ class FreqtradeBot(LoggingMixin): :param exit_check: CheckTuple with signal and reason :return: True if it succeeds False """ - trade.funding_fees = self.exchange.get_funding_fees( - pair=trade.pair, - amount=trade.amount, - is_short=trade.is_short, - open_date=trade.date_last_filled_utc, - ) + try: + trade.funding_fees = self.exchange.get_funding_fees( + pair=trade.pair, + amount=trade.amount, + is_short=trade.is_short, + open_date=trade.date_last_filled_utc, + ) + except ExchangeError: + logger.warning("Could not update funding fee.") + exit_type = 'exit' exit_reason = exit_tag or exit_check.exit_reason if exit_check.exit_type in ( From 39b6cadd14e8253541c2836daed435059d20670d Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 8 Sep 2022 07:24:57 +0200 Subject: [PATCH 07/16] Test keyerror case for funding_Fee calculation --- freqtrade/exchange/exchange.py | 2 +- tests/exchange/test_exchange.py | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index be5af91db..33a56c530 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -2514,7 +2514,7 @@ class Exchange: funding_rates = candle_histories[funding_comb] mark_rates = candle_histories[mark_comb] except KeyError: - raise ExchangeError("Could not find funding rates") from None + raise ExchangeError("Could not find funding rates.") from None funding_mark_rates = self.combine_funding_and_mark( funding_rates=funding_rates, mark_rates=mark_rates) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 3b903f8ee..71690ecdf 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -11,8 +11,9 @@ import pytest from pandas import DataFrame from freqtrade.enums import CandleType, MarginMode, TradingMode -from freqtrade.exceptions import (DDosProtection, DependencyException, InvalidOrderException, - OperationalException, PricingError, TemporaryError) +from freqtrade.exceptions import (DDosProtection, DependencyException, ExchangeError, + InvalidOrderException, OperationalException, PricingError, + TemporaryError) from freqtrade.exchange import (Binance, Bittrex, Exchange, Kraken, amount_to_precision, date_minus_candles, market_is_active, price_to_precision, timeframe_to_minutes, timeframe_to_msecs, timeframe_to_next_date, @@ -4179,17 +4180,24 @@ def test__fetch_and_calculate_funding_fees( type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True}) type(api_mock).has = PropertyMock(return_value={'fetchFundingRateHistory': True}) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange) + ex = get_patched_exchange(mocker, default_conf, api_mock, id=exchange) mocker.patch('freqtrade.exchange.Exchange.timeframes', PropertyMock( return_value=['1h', '4h', '8h'])) - funding_fees = exchange._fetch_and_calculate_funding_fees( + funding_fees = ex._fetch_and_calculate_funding_fees( pair='ADA/USDT', amount=amount, is_short=True, open_date=d1, close_date=d2) assert pytest.approx(funding_fees) == expected_fees # Fees for Longs are inverted - funding_fees = exchange._fetch_and_calculate_funding_fees( + funding_fees = ex._fetch_and_calculate_funding_fees( pair='ADA/USDT', amount=amount, is_short=False, open_date=d1, close_date=d2) assert pytest.approx(funding_fees) == -expected_fees + # Return empty "refresh_latest" + mocker.patch("freqtrade.exchange.Exchange.refresh_latest_ohlcv", return_value={}) + ex = get_patched_exchange(mocker, default_conf, api_mock, id=exchange) + with pytest.raises(ExchangeError, match="Could not find funding rates."): + ex._fetch_and_calculate_funding_fees( + pair='ADA/USDT', amount=amount, is_short=False, open_date=d1, close_date=d2) + @pytest.mark.parametrize('exchange,expected_fees', [ ('binance', -0.0009140999999999999), From 791f61c0899647f3c04ac072305e73187e266268 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 8 Sep 2022 07:13:15 +0000 Subject: [PATCH 08/16] Add test case for funding fee update failure --- freqtrade/freqtradebot.py | 2 -- tests/test_freqtradebot.py | 10 ++++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 61c323ed3..269c562d7 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -292,8 +292,6 @@ class FreqtradeBot(LoggingMixin): trade.funding_fees = funding_fees except ExchangeError: logger.warning("Could not update funding fees for open trades.") - else: - return 0.0 def startup_backpopulate_precision(self): diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 30b0b75b6..5f943504c 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -5430,6 +5430,16 @@ def test_update_funding_fees( )) +def test_update_funding_fees_error(mocker, default_conf, caplog): + mocker.patch('freqtrade.exchange.Exchange.get_funding_fees', side_effect=ExchangeError()) + default_conf['trading_mode'] = 'futures' + default_conf['margin_mode'] = 'isolated' + freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade.update_funding_fees() + + log_has("Could not update funding fees for open trades.", caplog) + + def test_position_adjust(mocker, default_conf_usdt, fee) -> None: patch_RPCManager(mocker) patch_exchange(mocker) From 9ef0ffe277e40159c207bb19782504d4bff492ff Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 8 Sep 2022 07:19:17 +0000 Subject: [PATCH 09/16] Update tests for funding-Fee exceptions --- freqtrade/freqtradebot.py | 3 ++- tests/test_freqtradebot.py | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 269c562d7..6c001a8d6 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -740,11 +740,12 @@ class FreqtradeBot(LoggingMixin): # This is a new trade if trade is None: + funding_fees = 0.0 try: funding_fees = self.exchange.get_funding_fees( pair=pair, amount=amount, is_short=is_short, open_date=open_date) except ExchangeError: - logger.warning("Could not update funding fee.") + logger.warning("Could not find funding fee.") trade = Trade( pair=pair, diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 5f943504c..565797d81 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -506,7 +506,7 @@ def test_create_trades_multiple_trades( def test_create_trades_preopen(default_conf_usdt, ticker_usdt, fee, mocker, - limit_buy_order_usdt_open) -> None: + limit_buy_order_usdt_open, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) default_conf_usdt['max_open_trades'] = 4 @@ -515,6 +515,7 @@ def test_create_trades_preopen(default_conf_usdt, ticker_usdt, fee, mocker, fetch_ticker=ticker_usdt, create_order=MagicMock(return_value=limit_buy_order_usdt_open), get_fee=fee, + get_funding_fees=MagicMock(side_effect=ExchangeError()), ) freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade) @@ -522,6 +523,7 @@ def test_create_trades_preopen(default_conf_usdt, ticker_usdt, fee, mocker, # Create 2 existing trades freqtrade.execute_entry('ETH/USDT', default_conf_usdt['stake_amount']) freqtrade.execute_entry('NEO/BTC', default_conf_usdt['stake_amount']) + assert log_has("Could not find funding fee.", caplog) assert len(Trade.get_open_trades()) == 2 # Change order_id for new orders @@ -3666,7 +3668,7 @@ def test_may_execute_trade_exit_after_stoploss_on_exchange_hit( (True, 29.70297029, 2.2, 2.3, -8.63762376, -0.1443212, 'loss'), ]) def test_execute_trade_exit_market_order( - default_conf_usdt, ticker_usdt, fee, is_short, current_rate, amount, + default_conf_usdt, ticker_usdt, fee, is_short, current_rate, amount, caplog, limit, profit_amount, profit_ratio, profit_or_loss, ticker_usdt_sell_up, mocker ) -> None: """ @@ -3694,6 +3696,7 @@ def test_execute_trade_exit_market_order( fetch_ticker=ticker_usdt, get_fee=fee, _is_dry_limit_order_filled=MagicMock(return_value=True), + get_funding_fees=MagicMock(side_effect=ExchangeError()), ) patch_whitelist(mocker, default_conf_usdt) freqtrade = FreqtradeBot(default_conf_usdt) @@ -3719,6 +3722,7 @@ def test_execute_trade_exit_market_order( limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'], exit_check=ExitCheckTuple(exit_type=ExitType.ROI) ) + assert log_has("Could not update funding fee.", caplog) assert not trade.is_open assert pytest.approx(trade.close_profit) == profit_ratio From 5e42defafc005de4d475776ae86ce6e242e8c8ed Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 8 Sep 2022 07:36:17 +0000 Subject: [PATCH 10/16] Update telegram docs to disable partial exit notifications --- docs/telegram-usage.md | 91 +++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index ece8700de..b9324def4 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -90,7 +90,8 @@ Example configuration showing the different settings: "trailing_stop_loss": "on", "stop_loss": "on", "stoploss_on_exchange": "on", - "custom_exit": "silent" + "custom_exit": "silent", + "partial_exit": "on" }, "entry_cancel": "silent", "exit_cancel": "on", @@ -138,7 +139,7 @@ You can create your own keyboard in `config.json`: "enabled": true, "token": "your_telegram_token", "chat_id": "your_telegram_chat_id", - "keyboard": [ + "keyboard": [ ["/daily", "/stats", "/balance", "/profit"], ["/status table", "/performance"], ["/reload_config", "/count", "/logs"] @@ -225,16 +226,16 @@ Once all positions are sold, run `/stop` to completely stop the bot. For each open trade, the bot will send you the following message. Enter Tag is configurable via Strategy. -> **Trade ID:** `123` `(since 1 days ago)` -> **Current Pair:** CVC/BTC +> **Trade ID:** `123` `(since 1 days ago)` +> **Current Pair:** CVC/BTC > **Direction:** Long > **Leverage:** 1.0 -> **Amount:** `26.64180098` +> **Amount:** `26.64180098` > **Enter Tag:** Awesome Long Signal -> **Open Rate:** `0.00007489` -> **Current Rate:** `0.00007489` -> **Current Profit:** `12.95%` -> **Stoploss:** `0.00007389 (-0.02%)` +> **Open Rate:** `0.00007489` +> **Current Rate:** `0.00007489` +> **Current Profit:** `12.95%` +> **Stoploss:** `0.00007389 (-0.02%)` ### /status table @@ -261,26 +262,26 @@ current max Return a summary of your profit/loss and performance. -> **ROI:** Close trades -> ∙ `0.00485701 BTC (2.2%) (15.2 Σ%)` -> ∙ `62.968 USD` -> **ROI:** All trades -> ∙ `0.00255280 BTC (1.5%) (6.43 Σ%)` -> ∙ `33.095 EUR` -> -> **Total Trade Count:** `138` -> **First Trade opened:** `3 days ago` -> **Latest Trade opened:** `2 minutes ago` -> **Avg. Duration:** `2:33:45` -> **Best Performing:** `PAY/BTC: 50.23%` -> **Trading volume:** `0.5 BTC` -> **Profit factor:** `1.04` -> **Max Drawdown:** `9.23% (0.01255 BTC)` +> **ROI:** Close trades +> ∙ `0.00485701 BTC (2.2%) (15.2 Σ%)` +> ∙ `62.968 USD` +> **ROI:** All trades +> ∙ `0.00255280 BTC (1.5%) (6.43 Σ%)` +> ∙ `33.095 EUR` +> +> **Total Trade Count:** `138` +> **First Trade opened:** `3 days ago` +> **Latest Trade opened:** `2 minutes ago` +> **Avg. Duration:** `2:33:45` +> **Best Performing:** `PAY/BTC: 50.23%` +> **Trading volume:** `0.5 BTC` +> **Profit factor:** `1.04` +> **Max Drawdown:** `9.23% (0.01255 BTC)` -The relative profit of `1.2%` is the average profit per trade. -The relative profit of `15.2 Σ%` is be based on the starting capital - so in this case, the starting capital was `0.00485701 * 1.152 = 0.00738 BTC`. -Starting capital is either taken from the `available_capital` setting, or calculated by using current wallet size - profits. -Profit Factor is calculated as gross profits / gross losses - and should serve as an overall metric for the strategy. +The relative profit of `1.2%` is the average profit per trade. +The relative profit of `15.2 Σ%` is be based on the starting capital - so in this case, the starting capital was `0.00485701 * 1.152 = 0.00738 BTC`. +Starting capital is either taken from the `available_capital` setting, or calculated by using current wallet size - profits. +Profit Factor is calculated as gross profits / gross losses - and should serve as an overall metric for the strategy. Max drawdown corresponds to the backtesting metric `Absolute Drawdown (Account)` - calculated as `(Absolute Drawdown) / (DrawdownHigh + startingBalance)`. ### /forceexit @@ -309,27 +310,27 @@ Note that for this to work, `force_entry_enable` needs to be set to true. ### /performance Return the performance of each crypto-currency the bot has sold. -> Performance: -> 1. `RCN/BTC 0.003 BTC (57.77%) (1)` -> 2. `PAY/BTC 0.0012 BTC (56.91%) (1)` -> 3. `VIB/BTC 0.0011 BTC (47.07%) (1)` -> 4. `SALT/BTC 0.0010 BTC (30.24%) (1)` -> 5. `STORJ/BTC 0.0009 BTC (27.24%) (1)` -> ... +> Performance: +> 1. `RCN/BTC 0.003 BTC (57.77%) (1)` +> 2. `PAY/BTC 0.0012 BTC (56.91%) (1)` +> 3. `VIB/BTC 0.0011 BTC (47.07%) (1)` +> 4. `SALT/BTC 0.0010 BTC (30.24%) (1)` +> 5. `STORJ/BTC 0.0009 BTC (27.24%) (1)` +> ... ### /balance Return the balance of all crypto-currency your have on the exchange. -> **Currency:** BTC -> **Available:** 3.05890234 -> **Balance:** 3.05890234 -> **Pending:** 0.0 +> **Currency:** BTC +> **Available:** 3.05890234 +> **Balance:** 3.05890234 +> **Pending:** 0.0 -> **Currency:** CVC -> **Available:** 86.64180098 -> **Balance:** 86.64180098 -> **Pending:** 0.0 +> **Currency:** CVC +> **Available:** 86.64180098 +> **Balance:** 86.64180098 +> **Pending:** 0.0 ### /daily @@ -376,7 +377,7 @@ Month (count) Profit BTC Profit USD Profit % Shows the current whitelist -> Using whitelist `StaticPairList` with 22 pairs +> Using whitelist `StaticPairList` with 22 pairs > `IOTA/BTC, NEO/BTC, TRX/BTC, VET/BTC, ADA/BTC, ETC/BTC, NCASH/BTC, DASH/BTC, XRP/BTC, XVG/BTC, EOS/BTC, LTC/BTC, OMG/BTC, BTG/BTC, LSK/BTC, ZEC/BTC, HOT/BTC, IOTX/BTC, XMR/BTC, AST/BTC, XLM/BTC, NANO/BTC` ### /blacklist [pair] @@ -386,7 +387,7 @@ If Pair is set, then this pair will be added to the pairlist. Also supports multiple pairs, separated by a space. Use `/reload_config` to reset the blacklist. -> Using blacklist `StaticPairList` with 2 pairs +> Using blacklist `StaticPairList` with 2 pairs >`DODGE/BTC`, `HOT/BTC`. ### /edge From 08726a264be3abdb69569170915c23cd1459c0b2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 8 Sep 2022 07:48:36 +0000 Subject: [PATCH 11/16] Update FAQ to point out multiple parallel positions per trade --- docs/faq.md | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 381bbceb5..a72268ef9 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -4,7 +4,7 @@ Freqtrade supports spot trading only. -### Can I open short positions? +### Can my bot open short positions? Freqtrade can open short positions in futures markets. This requires the strategy to be made for this - and `"trading_mode": "futures"` in the configuration. @@ -12,9 +12,9 @@ Please make sure to read the [relevant documentation page](leverage.md) first. In spot markets, you can in some cases use leveraged spot tokens, which reflect an inverted pair (eg. BTCUP/USD, BTCDOWN/USD, ETHBULL/USD, ETHBEAR/USD,...) which can be traded with Freqtrade. -### Can I trade options or futures? +### Can my bot trade options or futures? -Futures trading is supported for selected exchanges. +Futures trading is supported for selected exchanges. Please refer to the [documentation start page](index.md#supported-futures-exchanges-experimental) for an uptodate list of supported exchanges. ## Beginner Tips & Tricks @@ -22,6 +22,13 @@ Futures trading is supported for selected exchanges. ## Freqtrade common issues +### Can freqtrade open multiple positions on the same pair in parallel? + +No. Freqtrade will only open one position per pair at a time. +You can however use the [`adjust_trade_position()` callback](strategy-callbacks.md#adjust-trade-position) to adjust an open position. + +Backtesting provides an option for this in `--eps` - however this is only there to highlight "hidden" signals, and will not work in live. + ### The bot does not start Running the bot with `freqtrade trade --config config.json` shows the output `freqtrade: command not found`. @@ -30,7 +37,7 @@ This could be caused by the following reasons: * The virtual environment is not active. * Run `source .env/bin/activate` to activate the virtual environment. -* The installation did not work correctly. +* The installation did not complete successfully. * Please check the [Installation documentation](installation.md). ### I have waited 5 minutes, why hasn't the bot made any trades yet? @@ -67,7 +74,7 @@ This is not a bot-problem, but will also happen while manual trading. While freqtrade can handle this (it'll sell 99 COIN), fees are often below the minimum tradable lot-size (you can only trade full COIN, not 0.9 COIN). Leaving the dust (0.9 COIN) on the exchange makes usually sense, as the next time freqtrade buys COIN, it'll eat into the remaining small balance, this time selling everything it bought, and therefore slowly declining the dust balance (although it most likely will never reach exactly 0). -Where possible (e.g. on binance), the use of the exchange's dedicated fee currency will fix this. +Where possible (e.g. on binance), the use of the exchange's dedicated fee currency will fix this. On binance, it's sufficient to have BNB in your account, and have "Pay fees in BNB" enabled in your profile. Your BNB balance will slowly decline (as it's used to pay fees) - but you'll no longer encounter dust (Freqtrade will include the fees in the profit calculations). Other exchanges don't offer such possibilities, where it's simply something you'll have to accept or move to a different exchange. @@ -109,7 +116,7 @@ This warning can point to one of the below problems: ### I'm getting the "RESTRICTED_MARKET" message in the log -Currently known to happen for US Bittrex users. +Currently known to happen for US Bittrex users. Read [the Bittrex section about restricted markets](exchanges.md#restricted-markets) for more information. @@ -177,8 +184,8 @@ The GPU improvements would only apply to pandas-native calculations - or ones wr For hyperopt, freqtrade is using scikit-optimize, which is built on top of scikit-learn. Their statement about GPU support is [pretty clear](https://scikit-learn.org/stable/faq.html#will-you-add-gpu-support). -GPU's also are only good at crunching numbers (floating point operations). -For hyperopt, we need both number-crunching (find next parameters) and running python code (running backtesting). +GPU's also are only good at crunching numbers (floating point operations). +For hyperopt, we need both number-crunching (find next parameters) and running python code (running backtesting). As such, GPU's are not too well suited for most parts of hyperopt. The benefit of using GPU would therefore be pretty slim - and will not justify the complexity introduced by trying to add GPU support. @@ -219,9 +226,9 @@ already 8\*10^9\*10 evaluations. A roughly total of 80 billion evaluations. Did you run 100 000 evaluations? Congrats, you've done roughly 1 / 100 000 th of the search space, assuming that the bot never tests the same parameters more than once. -* The time it takes to run 1000 hyperopt epochs depends on things like: The available cpu, hard-disk, ram, timeframe, timerange, indicator settings, indicator count, amount of coins that hyperopt test strategies on and the resulting trade count - which can be 650 trades in a year or 100000 trades depending if the strategy aims for big profits by trading rarely or for many low profit trades. +* The time it takes to run 1000 hyperopt epochs depends on things like: The available cpu, hard-disk, ram, timeframe, timerange, indicator settings, indicator count, amount of coins that hyperopt test strategies on and the resulting trade count - which can be 650 trades in a year or 100000 trades depending if the strategy aims for big profits by trading rarely or for many low profit trades. -Example: 4% profit 650 times vs 0,3% profit a trade 10000 times in a year. If we assume you set the --timerange to 365 days. +Example: 4% profit 650 times vs 0,3% profit a trade 10000 times in a year. If we assume you set the --timerange to 365 days. Example: `freqtrade --config config.json --strategy SampleStrategy --hyperopt SampleHyperopt -e 1000 --timerange 20190601-20200601` From 92d71ebdb730c16bcab4eb61e3516ea7a58e4825 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Sep 2022 03:03:30 +0000 Subject: [PATCH 12/16] Bump python from 3.10.6-slim-bullseye to 3.10.7-slim-bullseye Bumps python from 3.10.6-slim-bullseye to 3.10.7-slim-bullseye. --- 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 e84a4d095..b3e5d5e88 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.10.6-slim-bullseye as base +FROM python:3.10.7-slim-bullseye as base # Setup env ENV LANG C.UTF-8 From d38cc061396d615832833fcbc880be5bdced8459 Mon Sep 17 00:00:00 2001 From: Wagner Costa Santos Date: Wed, 7 Sep 2022 18:52:58 -0300 Subject: [PATCH 13/16] Fix freqai backtesting time range issue --- freqtrade/freqai/data_kitchen.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/freqtrade/freqai/data_kitchen.py b/freqtrade/freqai/data_kitchen.py index 7670cfd45..01b2f1f3b 100644 --- a/freqtrade/freqai/data_kitchen.py +++ b/freqtrade/freqai/data_kitchen.py @@ -461,6 +461,20 @@ class FreqaiDataKitchen: return df + def remove_training_from_backtesting( + self + ) -> DataFrame: + """ + Function which takes the backtesting time range and + remove training data from dataframe + """ + tr = self.config["timerange"] + backtesting_timerange = TimeRange.parse_timerange(tr) + start = datetime.fromtimestamp(backtesting_timerange.startts, tz=timezone.utc) + df = self.return_dataframe + df = df.loc[df["date"] >= start, :] + return df + def principal_component_analysis(self) -> None: """ Performs Principal Component Analysis on the data for dimensionality reduction @@ -954,6 +968,7 @@ class FreqaiDataKitchen: to_keep = [col for col in dataframe.columns if not col.startswith("&")] self.return_dataframe = pd.concat([dataframe[to_keep], self.full_df], axis=1) + self.return_dataframe = self.remove_training_from_backtesting() self.full_df = DataFrame() return From 37fcbeba580143179c9344021ba2b5ceea516902 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 9 Sep 2022 19:58:55 +0200 Subject: [PATCH 14/16] Update backtesting output wording to Entries --- docs/backtesting.md | 74 ++++++++++++------------- freqtrade/optimize/optimize_reports.py | 5 +- tests/optimize/test_optimize_reports.py | 24 ++++---- 3 files changed, 52 insertions(+), 51 deletions(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index 8b2fdc345..f20a53d22 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -107,7 +107,7 @@ Strategy arguments: ## Test your strategy with Backtesting -Now you have good Buy and Sell strategies and some historic data, you want to test it against +Now you have good Entry and exit strategies and some historic data, you want to test it against real data. This is what we call [backtesting](https://en.wikipedia.org/wiki/Backtesting). Backtesting will use the crypto-currencies (pairs) from your config file and load historical candle (OHLCV) data from `user_data/data/` by default. @@ -215,7 +215,7 @@ Sometimes your account has certain fee rebates (fee reductions starting with a c To account for this in backtesting, you can use the `--fee` command line option to supply this value to backtesting. This fee must be a ratio, and will be applied twice (once for trade entry, and once for trade exit). -For example, if the buying and selling commission fee is 0.1% (i.e., 0.001 written as ratio), then you would run backtesting as the following: +For example, if the commission fee per order is 0.1% (i.e., 0.001 written as ratio), then you would run backtesting as the following: ```bash freqtrade backtesting --fee 0.001 @@ -252,41 +252,41 @@ The most important in the backtesting is to understand the result. A backtesting result will look like that: ``` -========================================================= BACKTESTING REPORT ========================================================== -| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Wins Draws Loss Win% | -|:---------|-------:|---------------:|---------------:|-----------------:|---------------:|:-------------|-------------------------:| -| ADA/BTC | 35 | -0.11 | -3.88 | -0.00019428 | -1.94 | 4:35:00 | 14 0 21 40.0 | -| ARK/BTC | 11 | -0.41 | -4.52 | -0.00022647 | -2.26 | 2:03:00 | 3 0 8 27.3 | -| BTS/BTC | 32 | 0.31 | 9.78 | 0.00048938 | 4.89 | 5:05:00 | 18 0 14 56.2 | -| DASH/BTC | 13 | -0.08 | -1.07 | -0.00005343 | -0.53 | 4:39:00 | 6 0 7 46.2 | -| ENG/BTC | 18 | 1.36 | 24.54 | 0.00122807 | 12.27 | 2:50:00 | 8 0 10 44.4 | -| EOS/BTC | 36 | 0.08 | 3.06 | 0.00015304 | 1.53 | 3:34:00 | 16 0 20 44.4 | -| ETC/BTC | 26 | 0.37 | 9.51 | 0.00047576 | 4.75 | 6:14:00 | 11 0 15 42.3 | -| ETH/BTC | 33 | 0.30 | 9.96 | 0.00049856 | 4.98 | 7:31:00 | 16 0 17 48.5 | -| IOTA/BTC | 32 | 0.03 | 1.09 | 0.00005444 | 0.54 | 3:12:00 | 14 0 18 43.8 | -| LSK/BTC | 15 | 1.75 | 26.26 | 0.00131413 | 13.13 | 2:58:00 | 6 0 9 40.0 | -| LTC/BTC | 32 | -0.04 | -1.38 | -0.00006886 | -0.69 | 4:49:00 | 11 0 21 34.4 | -| NANO/BTC | 17 | 1.26 | 21.39 | 0.00107058 | 10.70 | 1:55:00 | 10 0 7 58.5 | -| NEO/BTC | 23 | 0.82 | 18.97 | 0.00094936 | 9.48 | 2:59:00 | 10 0 13 43.5 | -| REQ/BTC | 9 | 1.17 | 10.54 | 0.00052734 | 5.27 | 3:47:00 | 4 0 5 44.4 | -| XLM/BTC | 16 | 1.22 | 19.54 | 0.00097800 | 9.77 | 3:15:00 | 7 0 9 43.8 | -| XMR/BTC | 23 | -0.18 | -4.13 | -0.00020696 | -2.07 | 5:30:00 | 12 0 11 52.2 | -| XRP/BTC | 35 | 0.66 | 22.96 | 0.00114897 | 11.48 | 3:49:00 | 12 0 23 34.3 | -| ZEC/BTC | 22 | -0.46 | -10.18 | -0.00050971 | -5.09 | 2:22:00 | 7 0 15 31.8 | -| TOTAL | 429 | 0.36 | 152.41 | 0.00762792 | 76.20 | 4:12:00 | 186 0 243 43.4 | +========================================================= BACKTESTING REPORT ========================================================= +| Pair | Entries | Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Wins Draws Loss Win% | +|:---------|--------:|---------------:|---------------:|-----------------:|---------------:|:-------------|-------------------------:| +| ADA/BTC | 35 | -0.11 | -3.88 | -0.00019428 | -1.94 | 4:35:00 | 14 0 21 40.0 | +| ARK/BTC | 11 | -0.41 | -4.52 | -0.00022647 | -2.26 | 2:03:00 | 3 0 8 27.3 | +| BTS/BTC | 32 | 0.31 | 9.78 | 0.00048938 | 4.89 | 5:05:00 | 18 0 14 56.2 | +| DASH/BTC | 13 | -0.08 | -1.07 | -0.00005343 | -0.53 | 4:39:00 | 6 0 7 46.2 | +| ENG/BTC | 18 | 1.36 | 24.54 | 0.00122807 | 12.27 | 2:50:00 | 8 0 10 44.4 | +| EOS/BTC | 36 | 0.08 | 3.06 | 0.00015304 | 1.53 | 3:34:00 | 16 0 20 44.4 | +| ETC/BTC | 26 | 0.37 | 9.51 | 0.00047576 | 4.75 | 6:14:00 | 11 0 15 42.3 | +| ETH/BTC | 33 | 0.30 | 9.96 | 0.00049856 | 4.98 | 7:31:00 | 16 0 17 48.5 | +| IOTA/BTC | 32 | 0.03 | 1.09 | 0.00005444 | 0.54 | 3:12:00 | 14 0 18 43.8 | +| LSK/BTC | 15 | 1.75 | 26.26 | 0.00131413 | 13.13 | 2:58:00 | 6 0 9 40.0 | +| LTC/BTC | 32 | -0.04 | -1.38 | -0.00006886 | -0.69 | 4:49:00 | 11 0 21 34.4 | +| NANO/BTC | 17 | 1.26 | 21.39 | 0.00107058 | 10.70 | 1:55:00 | 10 0 7 58.5 | +| NEO/BTC | 23 | 0.82 | 18.97 | 0.00094936 | 9.48 | 2:59:00 | 10 0 13 43.5 | +| REQ/BTC | 9 | 1.17 | 10.54 | 0.00052734 | 5.27 | 3:47:00 | 4 0 5 44.4 | +| XLM/BTC | 16 | 1.22 | 19.54 | 0.00097800 | 9.77 | 3:15:00 | 7 0 9 43.8 | +| XMR/BTC | 23 | -0.18 | -4.13 | -0.00020696 | -2.07 | 5:30:00 | 12 0 11 52.2 | +| XRP/BTC | 35 | 0.66 | 22.96 | 0.00114897 | 11.48 | 3:49:00 | 12 0 23 34.3 | +| ZEC/BTC | 22 | -0.46 | -10.18 | -0.00050971 | -5.09 | 2:22:00 | 7 0 15 31.8 | +| TOTAL | 429 | 0.36 | 152.41 | 0.00762792 | 76.20 | 4:12:00 | 186 0 243 43.4 | ========================================================= EXIT REASON STATS ========================================================== -| Exit Reason | Sells | Wins | Draws | Losses | +| Exit Reason | Exits | Wins | Draws | Losses | |:-------------------|--------:|------:|-------:|--------:| | trailing_stop_loss | 205 | 150 | 0 | 55 | | stop_loss | 166 | 0 | 0 | 166 | | exit_signal | 56 | 36 | 0 | 20 | | force_exit | 2 | 0 | 0 | 2 | ====================================================== LEFT OPEN TRADES REPORT ====================================================== -| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Win Draw Loss Win% | -|:---------|-------:|---------------:|---------------:|-----------------:|---------------:|:---------------|--------------------:| -| ADA/BTC | 1 | 0.89 | 0.89 | 0.00004434 | 0.44 | 6:00:00 | 1 0 0 100 | -| LTC/BTC | 1 | 0.68 | 0.68 | 0.00003421 | 0.34 | 2:00:00 | 1 0 0 100 | -| TOTAL | 2 | 0.78 | 1.57 | 0.00007855 | 0.78 | 4:00:00 | 2 0 0 100 | +| Pair | Entries | Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|:---------|---------:|---------------:|---------------:|-----------------:|---------------:|:---------------|--------------------:| +| ADA/BTC | 1 | 0.89 | 0.89 | 0.00004434 | 0.44 | 6:00:00 | 1 0 0 100 | +| LTC/BTC | 1 | 0.68 | 0.68 | 0.00003421 | 0.34 | 2:00:00 | 1 0 0 100 | +| TOTAL | 2 | 0.78 | 1.57 | 0.00007855 | 0.78 | 4:00:00 | 2 0 0 100 | ================== SUMMARY METRICS ================== | Metric | Value | |-----------------------------+---------------------| @@ -356,7 +356,7 @@ The column `Avg Profit %` shows the average profit for all trades made while the The column `Tot Profit %` shows instead the total profit % in relation to the starting balance. In the above results, we have a starting balance of 0.01 BTC and an absolute profit of 0.00762792 BTC - so the `Tot Profit %` will be `(0.00762792 / 0.01) * 100 ~= 76.2%`. -Your strategy performance is influenced by your buy strategy, your exit strategy, and also by the `minimal_roi` and `stop_loss` you have set. +Your strategy performance is influenced by your entry strategy, your exit strategy, and also by the `minimal_roi` and `stop_loss` you have set. For example, if your `minimal_roi` is only `"0": 0.01` you cannot expect the bot to make more profit than 1% (because it will exit every time a trade reaches 1%). @@ -515,7 +515,7 @@ You can then load the trades to perform further analysis as shown in the [data a Since backtesting lacks some detailed information about what happens within a candle, it needs to take a few assumptions: - Exchange [trading limits](#trading-limits-in-backtesting) are respected -- Buys happen at open-price +- Entries happen at open-price - All orders are filled at the requested price (no slippage, no unfilled orders) - Exit-signal exits happen at open-price of the consecutive candle - Exit-signal is favored over Stoploss, because exit-signals are assumed to trigger on candle's open @@ -612,11 +612,11 @@ There will be an additional table comparing win/losses of the different strategi Detailed output for all strategies one after the other will be available, so make sure to scroll up to see the details per strategy. ``` -=========================================================== STRATEGY SUMMARY ========================================================================= -| Strategy | Buys | Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Wins | Draws | Losses | Drawdown % | -|:------------|-------:|---------------:|---------------:|-----------------:|---------------:|:---------------|------:|-------:|-------:|-----------:| -| Strategy1 | 429 | 0.36 | 152.41 | 0.00762792 | 76.20 | 4:12:00 | 186 | 0 | 243 | 45.2 | -| Strategy2 | 1487 | -0.13 | -197.58 | -0.00988917 | -98.79 | 4:43:00 | 662 | 0 | 825 | 241.68 | +=========================================================== STRATEGY SUMMARY =========================================================================== +| Strategy | Entries | Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Wins | Draws | Losses | Drawdown % | +|:------------|---------:|---------------:|---------------:|-----------------:|---------------:|:---------------|------:|-------:|-------:|-----------:| +| Strategy1 | 429 | 0.36 | 152.41 | 0.00762792 | 76.20 | 4:12:00 | 186 | 0 | 243 | 45.2 | +| Strategy2 | 1487 | -0.13 | -197.58 | -0.00988917 | -98.79 | 4:43:00 | 662 | 0 | 825 | 241.68 | ``` ## Next step diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index 519022db2..fa6c3f161 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -75,7 +75,8 @@ def _get_line_floatfmt(stake_currency: str) -> List[str]: '.2f', 'd', 's', 's'] -def _get_line_header(first_column: str, stake_currency: str, direction: str = 'Buys') -> List[str]: +def _get_line_header(first_column: str, stake_currency: str, + direction: str = 'Entries') -> List[str]: """ Generate header lines (goes in line with _generate_result_line()) """ @@ -642,7 +643,7 @@ def text_table_tags(tag_type: str, tag_results: List[Dict[str, Any]], stake_curr if (tag_type == "enter_tag"): headers = _get_line_header("TAG", stake_currency) else: - headers = _get_line_header("TAG", stake_currency, 'Sells') + headers = _get_line_header("TAG", stake_currency, 'Exits') floatfmt = _get_line_floatfmt(stake_currency) output = [ [ diff --git a/tests/optimize/test_optimize_reports.py b/tests/optimize/test_optimize_reports.py index 562e12820..5095f2fde 100644 --- a/tests/optimize/test_optimize_reports.py +++ b/tests/optimize/test_optimize_reports.py @@ -40,14 +40,14 @@ def test_text_table_bt_results(): ) result_str = ( - '| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % |' - ' Avg Duration | Win Draw Loss Win% |\n' - '|---------+--------+----------------+----------------+------------------+----------------+' - '----------------+-------------------------|\n' - '| ETH/BTC | 3 | 8.33 | 25.00 | 0.50000000 | 12.50 |' - ' 0:20:00 | 2 0 1 66.7 |\n' - '| TOTAL | 3 | 8.33 | 25.00 | 0.50000000 | 12.50 |' - ' 0:20:00 | 2 0 1 66.7 |' + '| Pair | Entries | Avg Profit % | Cum Profit % | Tot Profit BTC | ' + 'Tot Profit % | Avg Duration | Win Draw Loss Win% |\n' + '|---------+-----------+----------------+----------------+------------------+' + '----------------+----------------+-------------------------|\n' + '| ETH/BTC | 3 | 8.33 | 25.00 | 0.50000000 | ' + '12.50 | 0:20:00 | 2 0 1 66.7 |\n' + '| TOTAL | 3 | 8.33 | 25.00 | 0.50000000 | ' + '12.50 | 0:20:00 | 2 0 1 66.7 |' ) pair_results = generate_pair_metrics(['ETH/BTC'], stake_currency='BTC', @@ -402,13 +402,13 @@ def test_text_table_strategy(testdatadir): bt_res_data_comparison = bt_res_data.pop('strategy_comparison') result_str = ( - '| Strategy | Buys | Avg Profit % | Cum Profit % | Tot Profit BTC |' + '| Strategy | Entries | Avg Profit % | Cum Profit % | Tot Profit BTC |' ' Tot Profit % | Avg Duration | Win Draw Loss Win% | Drawdown |\n' - '|----------------+--------+----------------+----------------+------------------+' + '|----------------+-----------+----------------+----------------+------------------+' '----------------+----------------+-------------------------+-----------------------|\n' - '| StrategyTestV2 | 179 | 0.08 | 14.39 | 0.02608550 |' + '| StrategyTestV2 | 179 | 0.08 | 14.39 | 0.02608550 |' ' 260.85 | 3:40:00 | 170 0 9 95.0 | 0.00308222 BTC 8.67% |\n' - '| TestStrategy | 179 | 0.08 | 14.39 | 0.02608550 |' + '| TestStrategy | 179 | 0.08 | 14.39 | 0.02608550 |' ' 260.85 | 3:40:00 | 170 0 9 95.0 | 0.00308222 BTC 8.67% |' ) From 97be3318f447445dfaab52f8b4d08ba2f8e59112 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 9 Sep 2022 20:17:34 +0200 Subject: [PATCH 15/16] Use Datetime_format from constants --- freqtrade/data/history/history_utils.py | 8 ++++---- freqtrade/freqai/freqai_interface.py | 5 +++-- tests/data/test_history.py | 6 +++--- tests/test_persistence.py | 10 +++++----- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/freqtrade/data/history/history_utils.py b/freqtrade/data/history/history_utils.py index 7a3fa4e0c..6a6e29429 100644 --- a/freqtrade/data/history/history_utils.py +++ b/freqtrade/data/history/history_utils.py @@ -228,9 +228,9 @@ def _download_pair_history(pair: str, *, ) logger.debug("Current Start: %s", - f"{data.iloc[0]['date']:%Y-%m-%d %H:%M:%S}" if not data.empty else 'None') + f"{data.iloc[0]['date']:DATETIME_PRINT_FORMAT}" if not data.empty else 'None') logger.debug("Current End: %s", - f"{data.iloc[-1]['date']:%Y-%m-%d %H:%M:%S}" if not data.empty else 'None') + f"{data.iloc[-1]['date']:DATETIME_PRINT_FORMAT}" if not data.empty else 'None') # Default since_ms to 30 days if nothing is given new_data = exchange.get_historic_ohlcv(pair=pair, @@ -254,9 +254,9 @@ def _download_pair_history(pair: str, *, fill_missing=False, drop_incomplete=False) logger.debug("New Start: %s", - f"{data.iloc[0]['date']:%Y-%m-%d %H:%M:%S}" if not data.empty else 'None') + f"{data.iloc[0]['date']:DATETIME_PRINT_FORMAT}" if not data.empty else 'None') logger.debug("New End: %s", - f"{data.iloc[-1]['date']:%Y-%m-%d %H:%M:%S}" if not data.empty else 'None') + f"{data.iloc[-1]['date']:DATETIME_PRINT_FORMAT}" if not data.empty else 'None') data_handler.ohlcv_store(pair, timeframe, data=data, candle_type=candle_type) return True diff --git a/freqtrade/freqai/freqai_interface.py b/freqtrade/freqai/freqai_interface.py index c5ac17a3a..7a48f6e15 100644 --- a/freqtrade/freqai/freqai_interface.py +++ b/freqtrade/freqai/freqai_interface.py @@ -14,6 +14,7 @@ from numpy.typing import NDArray from pandas import DataFrame from freqtrade.configuration import TimeRange +from freqtrade.constants import DATETIME_PRINT_FORMAT from freqtrade.enums import RunMode from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_seconds @@ -232,10 +233,10 @@ class IFreqaiModel(ABC): trained_timestamp = tr_train tr_train_startts_str = datetime.fromtimestamp( tr_train.startts, - tz=timezone.utc).strftime("%Y-%m-%d %H:%M:%S") + tz=timezone.utc).strftime(DATETIME_PRINT_FORMAT) tr_train_stopts_str = datetime.fromtimestamp( tr_train.stopts, - tz=timezone.utc).strftime("%Y-%m-%d %H:%M:%S") + tz=timezone.utc).strftime(DATETIME_PRINT_FORMAT) logger.info( f"Training {metadata['pair']}, {self.pair_it}/{self.total_pairs} pairs" f" from {tr_train_startts_str} to {tr_train_stopts_str}, {train_it}/{total_trains} " diff --git a/tests/data/test_history.py b/tests/data/test_history.py index 9709e7ad0..8081e984f 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -13,7 +13,7 @@ from pandas import DataFrame from pandas.testing import assert_frame_equal from freqtrade.configuration import TimeRange -from freqtrade.constants import AVAILABLE_DATAHANDLERS +from freqtrade.constants import AVAILABLE_DATAHANDLERS, DATETIME_PRINT_FORMAT from freqtrade.data.converter import ohlcv_to_dataframe from freqtrade.data.history.hdf5datahandler import HDF5DataHandler from freqtrade.data.history.history_utils import (_download_pair_history, _download_trades_history, @@ -386,7 +386,7 @@ def test_load_partial_missing(testdatadir, caplog) -> None: assert td != len(data['UNITTEST/BTC']) start_real = data['UNITTEST/BTC'].iloc[0, 0] assert log_has(f'UNITTEST/BTC, spot, 5m, ' - f'data starts at {start_real.strftime("%Y-%m-%d %H:%M:%S")}', + f'data starts at {start_real.strftime(DATETIME_PRINT_FORMAT)}', caplog) # Make sure we start fresh - test missing data at end caplog.clear() @@ -401,7 +401,7 @@ def test_load_partial_missing(testdatadir, caplog) -> None: # Shift endtime with +5 - as last candle is dropped (partial candle) end_real = arrow.get(data['UNITTEST/BTC'].iloc[-1, 0]).shift(minutes=5) assert log_has(f'UNITTEST/BTC, spot, 5m, ' - f'data ends at {end_real.strftime("%Y-%m-%d %H:%M:%S")}', + f'data ends at {end_real.strftime(DATETIME_PRINT_FORMAT)}', caplog) diff --git a/tests/test_persistence.py b/tests/test_persistence.py index cdca3bc4d..e7f218c02 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -9,7 +9,7 @@ import arrow import pytest from sqlalchemy import create_engine, text -from freqtrade import constants +from freqtrade.constants import DATETIME_PRINT_FORMAT, DEFAULT_DB_PROD_URL from freqtrade.enums import TradingMode from freqtrade.exceptions import DependencyException, OperationalException from freqtrade.persistence import LocalTrade, Order, Trade, init_db @@ -52,7 +52,7 @@ def test_init_invalid_db_url(): def test_init_prod_db(default_conf, mocker): default_conf.update({'dry_run': False}) - default_conf.update({'db_url': constants.DEFAULT_DB_PROD_URL}) + default_conf.update({'db_url': DEFAULT_DB_PROD_URL}) create_engine_mock = mocker.patch('freqtrade.persistence.models.create_engine', MagicMock()) @@ -1739,7 +1739,7 @@ def test_to_json(fee): 'base_currency': 'ADA', 'quote_currency': 'USDT', 'is_open': None, - 'open_date': trade.open_date.strftime("%Y-%m-%d %H:%M:%S"), + 'open_date': trade.open_date.strftime(DATETIME_PRINT_FORMAT), 'open_timestamp': int(trade.open_date.timestamp() * 1000), 'open_order_id': 'dry_run_buy_12345', 'close_date': None, @@ -1817,9 +1817,9 @@ def test_to_json(fee): 'pair': 'XRP/BTC', 'base_currency': 'XRP', 'quote_currency': 'BTC', - 'open_date': trade.open_date.strftime("%Y-%m-%d %H:%M:%S"), + 'open_date': trade.open_date.strftime(DATETIME_PRINT_FORMAT), 'open_timestamp': int(trade.open_date.timestamp() * 1000), - 'close_date': trade.close_date.strftime("%Y-%m-%d %H:%M:%S"), + 'close_date': trade.close_date.strftime(DATETIME_PRINT_FORMAT), 'close_timestamp': int(trade.close_date.timestamp() * 1000), 'open_rate': 0.123, 'close_rate': 0.125, From 939fb7acb338202dfa2bf945ee51d95e37e14e97 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 9 Sep 2022 20:31:30 +0200 Subject: [PATCH 16/16] Fix enum imports --- freqtrade/commands/db_commands.py | 2 +- freqtrade/exchange/okx.py | 3 +-- freqtrade/rpc/discord.py | 2 +- freqtrade/strategy/interface.py | 5 ++--- freqtrade/strategy/parameters.py | 2 +- tests/exchange/test_okx.py | 3 +-- tests/strategy/test_interface.py | 3 +-- 7 files changed, 8 insertions(+), 12 deletions(-) diff --git a/freqtrade/commands/db_commands.py b/freqtrade/commands/db_commands.py index 618b5cb6e..c424016b1 100644 --- a/freqtrade/commands/db_commands.py +++ b/freqtrade/commands/db_commands.py @@ -4,7 +4,7 @@ from typing import Any, Dict from sqlalchemy import func from freqtrade.configuration.config_setup import setup_utils_configuration -from freqtrade.enums.runmode import RunMode +from freqtrade.enums import RunMode logger = logging.getLogger(__name__) diff --git a/freqtrade/exchange/okx.py b/freqtrade/exchange/okx.py index 9340dd0e4..49f8ea107 100644 --- a/freqtrade/exchange/okx.py +++ b/freqtrade/exchange/okx.py @@ -4,8 +4,7 @@ from typing import Dict, List, Optional, Tuple import ccxt from freqtrade.constants import BuySell -from freqtrade.enums import MarginMode, TradingMode -from freqtrade.enums.candletype import CandleType +from freqtrade.enums import CandleType, MarginMode, TradingMode from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError from freqtrade.exchange import Exchange, date_minus_candles from freqtrade.exchange.common import retrier diff --git a/freqtrade/rpc/discord.py b/freqtrade/rpc/discord.py index 5991f7126..85acfae4e 100644 --- a/freqtrade/rpc/discord.py +++ b/freqtrade/rpc/discord.py @@ -1,7 +1,7 @@ import logging from typing import Any, Dict -from freqtrade.enums.rpcmessagetype import RPCMessageType +from freqtrade.enums import RPCMessageType from freqtrade.rpc import RPC from freqtrade.rpc.webhook import Webhook diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 70cc7fdb3..9401ebebe 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -12,9 +12,8 @@ from pandas import DataFrame from freqtrade.constants import ListPairsWithTimeframes from freqtrade.data.dataprovider import DataProvider -from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, SignalDirection, SignalTagType, - SignalType, TradingMode) -from freqtrade.enums.runmode import RunMode +from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, RunMode, SignalDirection, + SignalTagType, SignalType, TradingMode) from freqtrade.exceptions import OperationalException, StrategyError from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date, timeframe_to_seconds from freqtrade.persistence import Order, PairLocks, Trade diff --git a/freqtrade/strategy/parameters.py b/freqtrade/strategy/parameters.py index c6037ae0b..796fb9514 100644 --- a/freqtrade/strategy/parameters.py +++ b/freqtrade/strategy/parameters.py @@ -7,7 +7,7 @@ from abc import ABC, abstractmethod from contextlib import suppress from typing import Any, Optional, Sequence, Union -from freqtrade.enums.hyperoptstate import HyperoptState +from freqtrade.enums import HyperoptState from freqtrade.optimize.hyperopt_tools import HyperoptStateContainer diff --git a/tests/exchange/test_okx.py b/tests/exchange/test_okx.py index b475b84ff..12322acae 100644 --- a/tests/exchange/test_okx.py +++ b/tests/exchange/test_okx.py @@ -4,8 +4,7 @@ from unittest.mock import MagicMock, PropertyMock import pytest -from freqtrade.enums import MarginMode, TradingMode -from freqtrade.enums.candletype import CandleType +from freqtrade.enums import CandleType, MarginMode, TradingMode from freqtrade.exchange.exchange import timeframe_to_minutes from tests.conftest import get_mock_coro, get_patched_exchange, log_has from tests.exchange.test_exchange import ccxt_exceptionhandlers diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 65ee05d71..070e78b1d 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -11,8 +11,7 @@ from pandas import DataFrame from freqtrade.configuration import TimeRange from freqtrade.data.dataprovider import DataProvider from freqtrade.data.history import load_data -from freqtrade.enums import ExitCheckTuple, ExitType, SignalDirection -from freqtrade.enums.hyperoptstate import HyperoptState +from freqtrade.enums import ExitCheckTuple, ExitType, HyperoptState, SignalDirection from freqtrade.exceptions import OperationalException, StrategyError from freqtrade.optimize.hyperopt_tools import HyperoptStateContainer from freqtrade.optimize.space import SKDecimal